* [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage
@ 2026-08-13 6:27 Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown Tushar Vyavahare
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Tushar Vyavahare @ 2026-08-13 6:27 UTC (permalink / raw)
To: netdev, magnus.karlsson, maciej.fijalkowski, stfomichev,
kernelxing, davem, kuba, pabeni, ast, daniel, tirthendu.sarkar,
tushar.vyavahare, andrii
Cc: bpf
AF_XDP shared-UMEM support in the existing XSK selftests is limited to
a small set of scenarios, while the supporting setup and cleanup paths
become fragile as coverage expands.
This series improves shared-UMEM testing and infrastructure in five
steps.
Patch 1 makes UMEM ownership explicit with refcounting and centralizes
socket/UMEM teardown, keeping cleanup behavior consistent across normal
completion and error paths.
Patch 2 prevents TX setup when RX socket configuration has already
failed. This avoids shared-UMEM TX setup relying on incomplete RX-side
socket and UMEM state.
Patch 3 expands XSKMAP and test capacity to four sockets and adds a
length-based XDP steering program. The classifier uses total
XDP-visible packet length (data_end - data) and SHARED_UMEM_LEN_SPLIT.
Patch 4 adds the shared-UMEM callback runner and initial 4-socket,
length-based steering, uneven-distribution, and unaligned-chunk test
cases. The unaligned path snapshots and restores alignment flags with
NULL-safe UMEM guards.
Patch 5 makes pkt_stream_even_odd_sequence() transactional: it stages
replacement streams until all allocations succeed, avoiding leaked
replacement streams and partially replaced socket stream arrays after
an allocation failure.
Together, these patches strengthen shared-UMEM setup and cleanup and
extend selftest coverage to multi-socket and packet-distribution
scenarios.
v1 -> v2:
- [1/5] Track per-slot UMEM ownership and centralize refcount-based
socket teardown. [sashiko]
- [2/5] Replace the redundant partial-socket rollback with RX failure
publication before the setup barrier and skip TX setup after a failed
RX configuration. [BPF AI]
- [3/5] Trim the length-classifier comment to match nearby BPF style.
[BPF AI]
- [5/5] Clarify the partial stream replacement leak in the commit
message. [BPF AI]
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
Tushar Vyavahare (5):
selftests/xsk: add UMEM users refcount and centralize socket teardown
selftests/xsk: Skip TX setup after RX configuration failure
selftests/xsk: expand XSKMAP capacity and add length-based XDP program
selftests/xsk: add shared-UMEM callback framework and initial test
cases
selftests/xsk: make pkt_stream_even_odd_sequence rollback-safe
.../selftests/bpf/prog_tests/test_xsk.c | 423 ++++++++++++++++--
.../selftests/bpf/prog_tests/test_xsk.h | 26 +-
.../selftests/bpf/progs/xsk_xdp_progs.c | 15 +-
tools/testing/selftests/bpf/xsk_xdp_common.h | 3 +-
tools/testing/selftests/bpf/xskxceiver.c | 38 +-
5 files changed, 430 insertions(+), 75 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown
2026-08-13 6:27 [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage Tushar Vyavahare
@ 2026-08-13 6:27 ` Tushar Vyavahare
2026-08-14 6:30 ` sashiko-bot
2026-08-13 6:27 ` [PATCH net-next v2 2/5] selftests/xsk: Skip TX setup after RX configuration failure Tushar Vyavahare
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Tushar Vyavahare @ 2026-08-13 6:27 UTC (permalink / raw)
To: netdev, magnus.karlsson, maciej.fijalkowski, stfomichev,
kernelxing, davem, kuba, pabeni, ast, daniel, tirthendu.sarkar,
tushar.vyavahare, andrii
Cc: bpf
UMEM ownership is currently implicit: xsk_configure_umem() maps the
buffer, but freeing it is spread across testapp_clean_xsk_umem(),
clean_sockets() and clean_umem(), each called from a different error
path. Two of those paths get it wrong today:
- thread_common_ops() returns without releasing the UMEM when
xsk_configure() fails after the UMEM was already mapped, leaking
the mmap()ed buffer.
- the pthread_barrier_destroy() failure path in
__testapp_validate_traffic() unmaps ifobj1's UMEM while ifobj2's
sockets are left alive; with shared_umem those sockets reference
the buffer that was just unmapped.
Make ownership explicit instead. Add a refcount_t users field to
struct xsk_umem_info and release exactly one reference per socket on
teardown, so that the last socket to go away frees the UMEM regardless
of which path tears it down. Drop the "is ifobj2 sharing ifobj1's
UMEM?" special case from the callers, which is a prerequisite for the
shared-UMEM tests added later in this series.
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
---
.../selftests/bpf/prog_tests/test_xsk.c | 132 +++++++++++++-----
.../selftests/bpf/prog_tests/test_xsk.h | 5 +-
tools/testing/selftests/bpf/xskxceiver.c | 38 ++---
3 files changed, 114 insertions(+), 61 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index 4549358cc8c2..a3c77ab5f05c 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
@@ -101,6 +101,7 @@ int xsk_configure_umem(struct ifobject *ifobj, struct xsk_umem_info *umem, void
return ret;
umem->buffer = buffer;
+ refcount_set(&umem->users, 1);
if (ifobj->shared_umem && ifobj->rx_on) {
umem->base_addr = umem_size(umem);
umem->next_buffer = umem_size(umem);
@@ -154,6 +155,7 @@ int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem
struct xsk_socket_config cfg = {};
struct xsk_ring_cons *rxr;
struct xsk_ring_prod *txr;
+ int ret;
xsk->umem = umem;
cfg.rx_size = xsk->rxqsize;
@@ -170,7 +172,26 @@ int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem
txr = ifobject->tx_on ? &xsk->tx : NULL;
rxr = ifobject->rx_on ? &xsk->rx : NULL;
- return xsk_socket__create(&xsk->xsk, ifobject->ifindex, 0, umem->umem, rxr, txr, &cfg);
+ ret = xsk_socket__create(&xsk->xsk, ifobject->ifindex, 0, umem->umem, rxr, txr, &cfg);
+ if (ret) {
+ /*
+ * For shared sockets refcount_inc hasn't run yet, so clear umem to mark this slot
+ * as having no reference. For the owner (non-shared) the reference was taken by
+ * xsk_configure_umem; leave umem set so the caller's rollback path can release it
+ * via umem_ref.
+ */
+ if (shared)
+ xsk->umem = NULL;
+ xsk->xsk = NULL;
+ return ret;
+ }
+
+ if (shared) {
+ refcount_inc(&umem->users);
+ xsk->umem_ref = true;
+ }
+
+ return ret;
}
static int set_ring_size(struct ifobject *ifobj)
@@ -1467,6 +1488,17 @@ static int validate_tx_invalid_descs(struct ifobject *ifobject)
return TEST_PASS;
}
+static void xsk_delete_socket_batch(struct ifobject *ifobject, u32 count)
+{
+ u32 i;
+
+ if (!ifobject)
+ return;
+
+ for (i = count; i > 0; i--)
+ xsk_delete_socket(&ifobject->xsk_arr[i - 1]);
+}
+
static int xsk_configure(struct test_spec *test, struct ifobject *ifobject,
struct xsk_umem_info *umem, bool tx)
{
@@ -1599,6 +1631,8 @@ static int thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
ret = xsk_configure_umem(ifobject, umem, bufs, umem_sz);
if (ret)
return ret;
+ /* Mark before xsk_configure so rollback can release the UMEM if it fails. */
+ ifobject->xsk->umem_ref = true;
ret = xsk_configure(test, ifobject, umem, false);
if (ret)
@@ -1701,12 +1735,61 @@ void *worker_testapp_validate_rx(void *arg)
pthread_exit(NULL);
}
-static void testapp_clean_xsk_umem(struct ifobject *ifobj)
+void xsk_delete_socket(struct xsk_socket_info *xsk)
{
- struct xsk_umem_info *umem = ifobj->xsk->umem;
+ struct xsk_umem_info *umem;
+
+ if (!xsk)
+ return;
+
+ umem = xsk->umem;
+ if (!umem)
+ return;
+
+ if (xsk->xsk)
+ xsk_socket__delete(xsk->xsk);
+ xsk->xsk = NULL;
+
+ /* Skip slots that never acquired a UMEM reference (pre-initialized but unconfigured). */
+ if (!xsk->umem_ref) {
+ xsk->umem = NULL;
+ return;
+ }
- xsk_umem__delete(umem->umem);
- munmap(umem->buffer, umem->mmap_size);
+ if (refcount_dec_and_test(&umem->users)) {
+ if (umem->umem) {
+ int err = xsk_umem__delete(umem->umem);
+
+ if (err) {
+ ksft_print_msg("xsk_umem__delete failed: %d (umem still busy?)\n",
+ err);
+ /* Keep ownership explicit so a later cleanup pass can retry
+ * delete.
+ */
+ refcount_set(&umem->users, 1);
+ xsk->umem_ref = true;
+ xsk->umem = umem;
+ return;
+ }
+ umem->umem = NULL;
+ }
+ if (umem->buffer && umem->mmap_size) {
+ munmap(umem->buffer, umem->mmap_size);
+ umem->buffer = NULL;
+ umem->mmap_size = 0;
+ }
+ }
+
+ xsk->umem_ref = false;
+ xsk->umem = NULL;
+}
+
+static void xsk_delete_all_ifobj_sockets(struct test_spec *test, struct ifobject *ifobj)
+{
+ if (!ifobj)
+ return;
+
+ xsk_delete_socket_batch(ifobj, test->nb_sockets);
}
static bool xdp_prog_changed_rx(struct test_spec *test)
@@ -1768,27 +1851,6 @@ static int xsk_attach_xdp_progs(struct test_spec *test, struct ifobject *ifobj_r
return err;
}
-static void clean_sockets(struct test_spec *test, struct ifobject *ifobj)
-{
- u32 i;
-
- if (!ifobj || !test)
- return;
-
- for (i = 0; i < test->nb_sockets; i++)
- xsk_socket__delete(ifobj->xsk_arr[i].xsk);
-}
-
-static void clean_umem(struct test_spec *test, struct ifobject *ifobj1, struct ifobject *ifobj2)
-{
- if (!ifobj1)
- return;
-
- testapp_clean_xsk_umem(ifobj1);
- if (ifobj2 && !ifobj2->shared_umem)
- testapp_clean_xsk_umem(ifobj2);
-}
-
static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *ifobj1,
struct ifobject *ifobj2)
{
@@ -1840,8 +1902,7 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
if (pthread_barrier_destroy(&barr)) {
test->use_barrier = false;
pthread_join(t0, NULL);
- clean_sockets(test, ifobj1);
- clean_umem(test, ifobj1, NULL);
+ xsk_delete_all_ifobj_sockets(test, ifobj1);
return TEST_FAILURE;
}
}
@@ -1855,9 +1916,8 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
pthread_join(t0, NULL);
if (test->total_steps == test->current_step || test->fail) {
- clean_sockets(test, ifobj1);
- clean_sockets(test, ifobj2);
- clean_umem(test, ifobj1, ifobj2);
+ xsk_delete_all_ifobj_sockets(test, ifobj2);
+ xsk_delete_all_ifobj_sockets(test, ifobj1);
}
if (test->fail)
@@ -1966,9 +2026,8 @@ int testapp_xdp_prog_cleanup(struct test_spec *test)
return TEST_FAILURE;
if (swap_xsk_resources(test)) {
- clean_sockets(test, test->ifobj_rx);
- clean_sockets(test, test->ifobj_tx);
- clean_umem(test, test->ifobj_rx, test->ifobj_tx);
+ xsk_delete_all_ifobj_sockets(test, test->ifobj_tx);
+ xsk_delete_all_ifobj_sockets(test, test->ifobj_rx);
return TEST_FAILURE;
}
@@ -2506,9 +2565,8 @@ int testapp_hw_sw_max_ring_size(struct test_spec *test)
test->ifobj_tx->xsk->batch_size = test->ifobj_tx->ring.tx_max_pending - 8;
test->ifobj_rx->xsk->batch_size = test->ifobj_tx->ring.tx_max_pending - 8;
if (pkt_stream_replace(test, max_descs, MIN_PKT_SIZE)) {
- clean_sockets(test, test->ifobj_tx);
- clean_sockets(test, test->ifobj_rx);
- clean_umem(test, test->ifobj_rx, test->ifobj_tx);
+ xsk_delete_all_ifobj_sockets(test, test->ifobj_tx);
+ xsk_delete_all_ifobj_sockets(test, test->ifobj_rx);
return TEST_FAILURE;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.h b/tools/testing/selftests/bpf/prog_tests/test_xsk.h
index 03753ddc5dcd..5f98706ca0b5 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.h
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.h
@@ -4,6 +4,7 @@
#include <linux/ethtool.h>
#include <linux/if_xdp.h>
+#include <linux/refcount.h>
#include "../kselftest.h"
#include "xsk.h"
@@ -93,6 +94,7 @@ struct xsk_socket_info {
u8 dst_mac[ETH_ALEN];
u8 src_mac[ETH_ALEN];
bool check_consumer;
+ bool umem_ref; /* true if this slot holds a counted UMEM reference */
};
int kick_rx(struct xsk_socket_info *xsk);
@@ -104,6 +106,7 @@ struct xsk_umem_info {
struct xsk_umem *umem;
u64 next_buffer;
u64 mmap_size;
+ refcount_t users;
u32 num_frames;
u32 frame_headroom;
void *buffer;
@@ -159,7 +162,7 @@ int init_iface(struct ifobject *ifobj, thread_func_t func_ptr);
int xsk_configure_umem(struct ifobject *ifobj, struct xsk_umem_info *umem, void *buffer, u64 size);
int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
struct ifobject *ifobject, bool shared);
-
+void xsk_delete_socket(struct xsk_socket_info *xsk);
struct pkt {
int offset;
diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c
index 7dad8556a722..a86eaf141e93 100644
--- a/tools/testing/selftests/bpf/xskxceiver.c
+++ b/tools/testing/selftests/bpf/xskxceiver.c
@@ -117,12 +117,12 @@ static void __exit_with_error(int error, const char *file, const char *func, int
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
-static bool ifobj_zc_avail(struct ifobject *ifobject)
+static bool ifobj_zc_avail(struct ifobject *ifobj)
{
size_t umem_sz = DEFAULT_UMEM_BUFFERS * XSK_UMEM__DEFAULT_FRAME_SIZE;
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
- struct xsk_socket_info *xsk;
- struct xsk_umem_info *umem;
+ struct xsk_socket_info xsk = {};
+ struct xsk_umem_info umem = {};
bool zc_avail = false;
void *bufs;
int ret;
@@ -131,32 +131,24 @@ static bool ifobj_zc_avail(struct ifobject *ifobject)
if (bufs == MAP_FAILED)
exit_with_error(errno);
- umem = calloc(1, sizeof(struct xsk_umem_info));
- if (!umem) {
- munmap(bufs, umem_sz);
- exit_with_error(ENOMEM);
- }
- umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
- ret = xsk_configure_umem(ifobject, umem, bufs, umem_sz);
+ umem.mmap_size = umem_sz;
+ umem.frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
+ ret = xsk_configure_umem(ifobj, &umem, bufs, umem_sz);
if (ret)
exit_with_error(-ret);
- xsk = calloc(1, sizeof(struct xsk_socket_info));
- if (!xsk)
- goto out;
- ifobject->bind_flags = XDP_USE_NEED_WAKEUP | XDP_ZEROCOPY;
- ifobject->rx_on = true;
- xsk->rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
- ret = xsk_configure_socket(xsk, umem, ifobject, false);
+ xsk.umem_ref = true;
+
+ ifobj->bind_flags = XDP_USE_NEED_WAKEUP | XDP_ZEROCOPY;
+ ifobj->rx_on = true;
+ xsk.rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
+ ret = xsk_configure_socket(&xsk, &umem, ifobj, false);
if (!ret)
zc_avail = true;
- xsk_socket__delete(xsk->xsk);
- free(xsk);
-out:
- munmap(umem->buffer, umem_sz);
- xsk_umem__delete(umem->umem);
- free(umem);
+ /* Use the same refcount-based teardown path for both success and failure. */
+ xsk_delete_socket(&xsk);
+
return zc_avail;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 2/5] selftests/xsk: Skip TX setup after RX configuration failure
2026-08-13 6:27 [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown Tushar Vyavahare
@ 2026-08-13 6:27 ` Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 3/5] selftests/xsk: expand XSKMAP capacity and add length-based XDP program Tushar Vyavahare
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Tushar Vyavahare @ 2026-08-13 6:27 UTC (permalink / raw)
To: netdev, magnus.karlsson, maciej.fijalkowski, stfomichev,
kernelxing, davem, kuba, pabeni, ast, daniel, tirthendu.sarkar,
tushar.vyavahare, andrii
Cc: bpf
When RX socket setup fails during the first traffic step, the RX worker
previously reports test->fail only after waiting at the setup barrier. The
main thread can then start TX setup before it observes that failure.
For shared-UMEM tests, TX setup depends on the RX-side socket and UMEM
state having been configured successfully. Do not start TX setup after a
failed RX setup.
Set test->fail before the RX worker reaches the barrier and check it
before starting the TX thread. The existing post-join teardown cleans up
the RX socket slots and UMEM.
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
---
tools/testing/selftests/bpf/prog_tests/test_xsk.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index a3c77ab5f05c..6d3b2ecc957b 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
@@ -1703,12 +1703,15 @@ void *worker_testapp_validate_rx(void *arg)
strerror(-err));
}
+ /* Publish setup failure before releasing the main thread from the barrier. */
+ if (err)
+ test->fail = true;
+
if (test->use_barrier)
pthread_barrier_wait(&barr);
/* We leave only now in case of error to avoid getting stuck in the barrier */
if (err) {
- test->fail = true;
pthread_exit(NULL);
}
@@ -1907,7 +1910,7 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
}
}
- if (ifobj2) {
+ if (ifobj2 && !test->fail) {
/*Spawn TX thread */
pthread_create(&t1, NULL, ifobj2->func_ptr, test);
pthread_join(t1, NULL);
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 3/5] selftests/xsk: expand XSKMAP capacity and add length-based XDP program
2026-08-13 6:27 [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 2/5] selftests/xsk: Skip TX setup after RX configuration failure Tushar Vyavahare
@ 2026-08-13 6:27 ` Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 4/5] selftests/xsk: add shared-UMEM callback framework and initial test cases Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 5/5] selftests/xsk: make pkt_stream_even_odd_sequence rollback-safe Tushar Vyavahare
4 siblings, 0 replies; 7+ messages in thread
From: Tushar Vyavahare @ 2026-08-13 6:27 UTC (permalink / raw)
To: netdev, magnus.karlsson, maciej.fijalkowski, stfomichev,
kernelxing, davem, kuba, pabeni, ast, daniel, tirthendu.sarkar,
tushar.vyavahare, andrii
Cc: bpf
Raise MAX_SOCKETS from 2 to 4 so the XSKMAP and test arrays can
accommodate the upcoming 4-socket shared-UMEM test. Update the XSKMAP
max_entries to use MAX_SOCKETS so the BPF and C sides stay in sync.
Add xsk_xdp_shared_umem_length_based() XDP program that routes packets
by total XDP-visible packet length (data_end - data): socket 0 for
packets <= SHARED_UMEM_LEN_SPLIT bytes and socket 1 for packets >
SHARED_UMEM_LEN_SPLIT bytes. Define SHARED_UMEM_LEN_SPLIT as 64 to avoid
hardcoded length thresholds in the classifier.
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
---
tools/testing/selftests/bpf/progs/xsk_xdp_progs.c | 15 ++++++++++++++-
tools/testing/selftests/bpf/xsk_xdp_common.h | 3 ++-
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c b/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c
index 023d8befd4ca..8bab2bcf52ab 100644
--- a/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c
+++ b/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c
@@ -10,7 +10,7 @@
struct {
__uint(type, BPF_MAP_TYPE_XSKMAP);
- __uint(max_entries, 2);
+ __uint(max_entries, MAX_SOCKETS);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} xsk SEC(".maps");
@@ -75,6 +75,19 @@ SEC("xdp") int xsk_xdp_shared_umem(struct xdp_md *xdp)
return bpf_redirect_map(&xsk, idx, XDP_DROP);
}
+SEC("xdp") int xsk_xdp_shared_umem_length_based(struct xdp_md *xdp)
+{
+ void *data = (void *)(long)xdp->data;
+ void *data_end = (void *)(long)xdp->data_end;
+ __u32 pkt_len = data_end - data;
+
+ /* Route packets by total XDP-visible packet length. */
+ if (pkt_len <= SHARED_UMEM_LEN_SPLIT)
+ return bpf_redirect_map(&xsk, 0, XDP_DROP);
+ else
+ return bpf_redirect_map(&xsk, 1, XDP_DROP);
+}
+
SEC("xdp.frags") int xsk_xdp_adjust_tail(struct xdp_md *xdp)
{
__u32 buff_len, curr_buff_len;
diff --git a/tools/testing/selftests/bpf/xsk_xdp_common.h b/tools/testing/selftests/bpf/xsk_xdp_common.h
index 45810ff552da..bd9b5dcf8c8e 100644
--- a/tools/testing/selftests/bpf/xsk_xdp_common.h
+++ b/tools/testing/selftests/bpf/xsk_xdp_common.h
@@ -3,7 +3,8 @@
#ifndef XSK_XDP_COMMON_H_
#define XSK_XDP_COMMON_H_
-#define MAX_SOCKETS 2
+#define MAX_SOCKETS 4
+#define SHARED_UMEM_LEN_SPLIT 64
#define PKT_HDR_ALIGN (sizeof(struct ethhdr) + 2) /* Just to align the data in the packet */
struct xdp_info {
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 4/5] selftests/xsk: add shared-UMEM callback framework and initial test cases
2026-08-13 6:27 [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage Tushar Vyavahare
` (2 preceding siblings ...)
2026-08-13 6:27 ` [PATCH net-next v2 3/5] selftests/xsk: expand XSKMAP capacity and add length-based XDP program Tushar Vyavahare
@ 2026-08-13 6:27 ` Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 5/5] selftests/xsk: make pkt_stream_even_odd_sequence rollback-safe Tushar Vyavahare
4 siblings, 0 replies; 7+ messages in thread
From: Tushar Vyavahare @ 2026-08-13 6:27 UTC (permalink / raw)
To: netdev, magnus.karlsson, maciej.fijalkowski, stfomichev,
kernelxing, davem, kuba, pabeni, ast, daniel, tirthendu.sarkar,
tushar.vyavahare, andrii
Cc: bpf
Add runner callback infrastructure together with the first four
shared-UMEM test cases so each commit in the series builds clean.
In __test_spec_init(), shared_default derives shared_umem from TX/RX
ifindex equality so resets follow the same baseline behavior used by
xskxceiver startup. Add pkt_stream_len_seq() for alternating
short/long per-socket packet sizes, and pkt_stream_uneven_dist_seq() for
unequal packet volume across two sockets. Add run_shared_umem_test() as
the common runner that sets programs/maps, validates stream readiness,
runs the sequence callback, executes traffic, and optionally runs
post-validation. Define shared_umem_seq_fn, shared_umem_post_fn,
shared_umem_len_ctx, and shared_umem_uneven_dist_ctx in test_xsk.h for
shared use.
Add SHARED_UMEM_4_SOCKETS for a 4-socket even/odd split,
SHARED_UMEM_LENGTH_BASED for short-vs-long packet steering,
SHARED_UMEM_UNEVEN_DIST for a 1:3 packet-volume distribution check, and
SHARED_UMEM_UNALIGNED for even/odd sequencing in unaligned mode.
The unaligned test snapshots and restores prior alignment flags with
NULL-safe UMEM guards so behavior does not depend on test order.
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
---
.../selftests/bpf/prog_tests/test_xsk.c | 249 ++++++++++++++++++
.../selftests/bpf/prog_tests/test_xsk.h | 21 ++
2 files changed, 270 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index 6d3b2ecc957b..54143fc757e1 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
@@ -224,6 +224,12 @@ int hw_ring_size_reset(struct ifobject *ifobj)
static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
struct ifobject *ifobj_rx)
{
+ /*
+ * Keep the same default as xskxceiver startup: when TX and RX share the same netdev,
+ * shared UMEM is the baseline mode for this test harness. Individual tests can still
+ * override this as needed.
+ */
+ bool shared_default = ifobj_tx->ifindex == ifobj_rx->ifindex;
u32 i, j;
for (i = 0; i < MAX_INTERFACES; i++) {
@@ -235,6 +241,7 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
ifobj->use_fill_ring = true;
ifobj->release_rx = true;
ifobj->validation_func = NULL;
+ ifobj->shared_umem = shared_default;
ifobj->use_metadata = false;
if (i == 0) {
@@ -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;
+}
+
static void release_even_odd_sequence(struct test_spec *test)
{
struct pkt_stream *later_free_tx = test->ifobj_tx->xsk->pkt_stream;
@@ -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;
+}
+
+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;
+
+ ret = testapp_validate_traffic(test);
+ if (post_fn)
+ ret = post_fn(test, ret, ctx);
+
+ release_even_odd_sequence(test);
+
+ return ret;
+}
+
+int testapp_shared_umem_4_sockets(struct test_spec *test)
+{
+ struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
+ struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
+
+ return run_shared_umem_test(test, skel_rx->progs.xsk_xdp_shared_umem,
+ skel_tx->progs.xsk_xdp_shared_umem, skel_rx->maps.xsk,
+ skel_tx->maps.xsk, 4, shared_umem_seq_even_odd, NULL, NULL);
+}
+
+int testapp_shared_umem_length_based(struct test_spec *test)
+{
+ struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
+ struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
+ const struct shared_umem_len_ctx len_ctx = {
+ .short_len = MIN_PKT_SIZE,
+ .long_len = MIN_PKT_SIZE * 2,
+ };
+
+ return run_shared_umem_test(test, skel_rx->progs.xsk_xdp_shared_umem_length_based,
+ skel_tx->progs.xsk_xdp_shared_umem_length_based,
+ skel_rx->maps.xsk, skel_tx->maps.xsk, 2, shared_umem_seq_len,
+ NULL, &len_ctx);
+}
+
+int testapp_shared_umem_uneven_dist(struct test_spec *test)
+{
+ struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
+ struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
+ const struct shared_umem_uneven_dist_ctx uneven_dist_ctx = {
+ .total_pkts = DEFAULT_PKT_CNT * 4,
+ .pkt_len = MIN_PKT_SIZE,
+ };
+
+ return run_shared_umem_test(test, skel_rx->progs.xsk_xdp_shared_umem,
+ skel_tx->progs.xsk_xdp_shared_umem, skel_rx->maps.xsk,
+ skel_tx->maps.xsk, 2, shared_umem_seq_uneven_dist,
+ shared_umem_post_uneven_dist, &uneven_dist_ctx);
+}
+
+int testapp_shared_umem_unaligned(struct test_spec *test)
+{
+ struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
+ struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
+ struct xsk_umem_info *tx_umem = test->ifobj_tx && test->ifobj_tx->xsk ?
+ test->ifobj_tx->xsk->umem : NULL;
+ struct xsk_umem_info *rx_umem = test->ifobj_rx && test->ifobj_rx->xsk ?
+ test->ifobj_rx->xsk->umem : NULL;
+ bool tx_unaligned = tx_umem ? tx_umem->unaligned_mode : false;
+ bool rx_unaligned = rx_umem ? rx_umem->unaligned_mode : false;
+ int ret;
+
+ test_spec_set_unaligned(test);
+
+ ret = run_shared_umem_test(test, skel_rx->progs.xsk_xdp_shared_umem,
+ skel_tx->progs.xsk_xdp_shared_umem, skel_rx->maps.xsk,
+ skel_tx->maps.xsk, 2, shared_umem_seq_even_odd, NULL, NULL);
+
+ if (tx_umem)
+ tx_umem->unaligned_mode = tx_unaligned;
+ if (rx_umem)
+ rx_umem->unaligned_mode = rx_unaligned;
+
+ return ret;
+}
+
int testapp_poll_txq_tmout(struct test_spec *test)
{
bool shared_umem = test->ifobj_tx->shared_umem;
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.h b/tools/testing/selftests/bpf/prog_tests/test_xsk.h
index 5f98706ca0b5..93dcf66b4e62 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.h
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.h
@@ -80,6 +80,9 @@ struct test_spec;
typedef int (*validation_func_t)(struct ifobject *ifobj);
typedef void *(*thread_func_t)(void *arg);
typedef int (*test_func_t)(struct test_spec *test);
+typedef int (*shared_umem_seq_fn)(struct test_spec *test, const void *ctx);
+typedef int (*shared_umem_post_fn)(struct test_spec *test, int ret,
+ const void *ctx);
struct xsk_socket_info {
struct xsk_ring_cons rx;
@@ -182,6 +185,16 @@ struct pkt_stream {
bool verbatim;
};
+struct shared_umem_len_ctx {
+ u32 short_len;
+ u32 long_len;
+};
+
+struct shared_umem_uneven_dist_ctx {
+ u32 total_pkts;
+ u32 pkt_len;
+};
+
static inline bool pkt_continues(u32 options)
{
return options & XDP_PKT_CONTD;
@@ -271,6 +284,10 @@ int testapp_xdp_metadata(struct test_spec *test);
int testapp_xdp_metadata_mb(struct test_spec *test);
int testapp_xdp_prog_cleanup(struct test_spec *test);
int testapp_xdp_shared_umem(struct test_spec *test);
+int testapp_shared_umem_4_sockets(struct test_spec *test);
+int testapp_shared_umem_length_based(struct test_spec *test);
+int testapp_shared_umem_uneven_dist(struct test_spec *test);
+int testapp_shared_umem_unaligned(struct test_spec *test);
void *worker_testapp_validate_rx(void *arg);
void *worker_testapp_validate_tx(void *arg);
@@ -294,6 +311,10 @@ static const struct test_spec tests[] = {
{.name = "XDP_PROG_CLEANUP", .test_func = testapp_xdp_prog_cleanup},
{.name = "XDP_DROP_HALF", .test_func = testapp_xdp_drop},
{.name = "XDP_SHARED_UMEM", .test_func = testapp_xdp_shared_umem},
+ {.name = "SHARED_UMEM_4_SOCKETS", .test_func = testapp_shared_umem_4_sockets},
+ {.name = "SHARED_UMEM_LENGTH_BASED", .test_func = testapp_shared_umem_length_based},
+ {.name = "SHARED_UMEM_UNEVEN_DIST", .test_func = testapp_shared_umem_uneven_dist},
+ {.name = "SHARED_UMEM_UNALIGNED", .test_func = testapp_shared_umem_unaligned},
{.name = "XDP_METADATA_COPY", .test_func = testapp_xdp_metadata},
{.name = "XDP_METADATA_COPY_MULTI_BUFF", .test_func = testapp_xdp_metadata_mb},
{.name = "ALIGNED_INV_DESC_MULTI_BUFF", .test_func = testapp_aligned_inv_desc_mb},
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 5/5] selftests/xsk: make pkt_stream_even_odd_sequence rollback-safe
2026-08-13 6:27 [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage Tushar Vyavahare
` (3 preceding siblings ...)
2026-08-13 6:27 ` [PATCH net-next v2 4/5] selftests/xsk: add shared-UMEM callback framework and initial test cases Tushar Vyavahare
@ 2026-08-13 6:27 ` Tushar Vyavahare
4 siblings, 0 replies; 7+ messages in thread
From: Tushar Vyavahare @ 2026-08-13 6:27 UTC (permalink / raw)
To: netdev, magnus.karlsson, maciej.fijalkowski, stfomichev,
kernelxing, davem, kuba, pabeni, ast, daniel, tirthendu.sarkar,
tushar.vyavahare, andrii
Cc: bpf
pkt_stream_even_odd_sequence() replaces each socket stream as it is
generated. If a later allocation fails, the earlier replacement streams are
already published in xsk_arr while the original streams for those slots are
lost.
Both testapp_xdp_shared_umem() and run_shared_umem_test() return
immediately on that error, without calling release_even_odd_sequence().
This leaks the already-published replacement streams and leaves the socket
arrays with a partially replaced stream set.
Generate all TX and RX streams in temporary arrays and publish them only
after every allocation succeeds. On failure, free the temporary streams
and leave xsk_arr unchanged.
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
---
.../selftests/bpf/prog_tests/test_xsk.c | 35 +++++++++++++------
1 file changed, 25 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index 54143fc757e1..ae6a091b6485 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
@@ -602,26 +602,41 @@ static int pkt_stream_receive_half(struct test_spec *test)
static int pkt_stream_even_odd_sequence(struct test_spec *test)
{
+ 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++) {
pkt_stream = test->ifobj_tx->xsk_arr[i].pkt_stream;
- pkt_stream = __pkt_stream_generate(pkt_stream->nb_pkts / 2,
- pkt_stream->pkts[0].len, i, 2);
- if (!pkt_stream)
- return -ENOMEM;
- test->ifobj_tx->xsk_arr[i].pkt_stream = pkt_stream;
+ tx_streams[i] = __pkt_stream_generate(pkt_stream->nb_pkts / 2,
+ pkt_stream->pkts[0].len, i, 2);
+ if (!tx_streams[i])
+ goto err;
pkt_stream = test->ifobj_rx->xsk_arr[i].pkt_stream;
- pkt_stream = __pkt_stream_generate(pkt_stream->nb_pkts / 2,
- pkt_stream->pkts[0].len, i, 2);
- if (!pkt_stream)
- return -ENOMEM;
- test->ifobj_rx->xsk_arr[i].pkt_stream = pkt_stream;
+ rx_streams[i] = __pkt_stream_generate(pkt_stream->nb_pkts / 2,
+ pkt_stream->pkts[0].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_len_seq(struct test_spec *test, u32 short_len, u32 long_len)
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown
2026-08-13 6:27 ` [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown Tushar Vyavahare
@ 2026-08-14 6:30 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-14 6:30 UTC (permalink / raw)
To: Tushar Vyavahare; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Duplicated refcount_t via memcpy breaks shared UMEM ownership, causing a double-free.
- [Medium] Stack-allocated structs in ifobj_zc_avail() cause a memory leak when xsk_umem__delete fails.
--
--- Patch [1]: [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown ---
commit ae6d3807f03b65dcec34132aba330f0371725f42
Author: Tushar Vyavahare <tushar.vyavahare@intel.com>
selftests/xsk: add UMEM users refcount and centralize socket teardown
UMEM ownership is currently implicit, with freeing spread across multiple
error paths, which has led to leaks or use-after-free bugs. This commit
makes ownership explicit by adding a refcount_t users field to struct
xsk_umem_info. It centralizes socket teardown so the last socket to go
away frees the UMEM.
> diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
> index 38ce6060b8fac..d0c2d0efb97f5 100644
> --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
> +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
> @@ -101,6 +101,7 @@ int xsk_configure_umem(struct ifobject *ifobj, struct xsk_umem_info *umem, void
> return ret;
>
> umem->buffer = buffer;
> + refcount_set(&umem->users, 1);
[Severity: Medium]
Will this lead to a double free of the shared UMEM?
In thread_common_ops_tx(), the umem structure is copied using memcpy:
umem_tx = ifobject->xsk_arr[0].umem_real;
memcpy(umem_tx, umem_rx, sizeof(*umem_tx));
Since this duplicates the new refcount_t users field, the reference count
between the TX and RX UMEM structures is decoupled.
When the test tears down, both TX and RX sockets will independently
decrement their separate UMEM refcounts to zero, causing
xsk_umem__delete() and munmap() to be executed twice on the same pointers.
> if (ifobj->shared_umem && ifobj->rx_on) {
> umem->base_addr = umem_size(umem);
> umem->next_buffer = umem_size(umem);
[ ... ]
> diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c
> index 7dad8556a722e..a86eaf141e931 100644
> --- a/tools/testing/selftests/bpf/xskxceiver.c
> +++ b/tools/testing/selftests/bpf/xskxceiver.c
> @@ -117,12 +117,12 @@ static void __exit_with_error(int error, const char *file, const char *func, int
>
> #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
>
> -static bool ifobj_zc_avail(struct ifobject *ifobject)
> +static bool ifobj_zc_avail(struct ifobject *ifobj)
> {
> size_t umem_sz = DEFAULT_UMEM_BUFFERS * XSK_UMEM__DEFAULT_FRAME_SIZE;
> int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
> - struct xsk_socket_info *xsk;
> - struct xsk_umem_info *umem;
> + struct xsk_socket_info xsk = {};
> + struct xsk_umem_info umem = {};
[Severity: Medium]
Could this result in a memory leak if xsk_umem__delete() fails?
The xsk and umem structures are now allocated on the stack. The new cleanup
logic in xsk_delete_socket() defers cleanup if xsk_umem__delete() fails:
if (refcount_dec_and_test(&umem->users)) {
if (umem->umem) {
int err = xsk_umem__delete(umem->umem);
if (err) {
...
/* Keep ownership explicit so a later cleanup pass can retry */
refcount_set(&umem->users, 1);
xsk->umem_ref = true;
xsk->umem = umem;
return;
}
If xsk_umem__delete() returns an error, xsk_delete_socket() returns early
and defers munmap() by leaving state in the structure.
Because the structs are stack-allocated, they are destroyed when
ifobj_zc_avail() returns. The deferred retry can never happen, meaning the
mmapped memory is permanently leaked.
> bool zc_avail = false;
> void *bufs;
> int ret;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260813062754.360131-1-tushar.vyavahare@intel.com?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-14 6:30 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 6:27 [PATCH net-next v2 0/5] selftests/xsk: improve shared-UMEM coverage Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 1/5] selftests/xsk: add UMEM users refcount and centralize socket teardown Tushar Vyavahare
2026-08-14 6:30 ` sashiko-bot
2026-08-13 6:27 ` [PATCH net-next v2 2/5] selftests/xsk: Skip TX setup after RX configuration failure Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 3/5] selftests/xsk: expand XSKMAP capacity and add length-based XDP program Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 4/5] selftests/xsk: add shared-UMEM callback framework and initial test cases Tushar Vyavahare
2026-08-13 6:27 ` [PATCH net-next v2 5/5] selftests/xsk: make pkt_stream_even_odd_sequence rollback-safe Tushar Vyavahare
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.