[PATCH net 4/4] selftests: drv-net: add HDS payload sweep test for devmem TCP
From: Bobby Eshleman
Date: Wed Feb 11 2026 - 20:04:41 EST
From: Bobby Eshleman <bobbyeshleman@xxxxxxxx>
Add check_rx_hds test that verifies header/data split works across
payload sizes. The test sweeps payload sizes from 1 byte to 8KB, if any
data propagates up to userspace as SCM_DEVMEM_LINEAR, then the test
fails. This shows that regardless of payload size, ncdevmem's
configuration of hds-thresh to 0 is respected.
Add -L (--fail-on-linear) flag to ncdevmem that causes the receiver to
fail if any SCM_DEVMEM_LINEAR cmsg is received.
Use socat option for fixed block sizing and tcp nodelay to disable
nagle's algo to avoid buffering.
Signed-off-by: Bobby Eshleman <bobbyeshleman@xxxxxxxx>
---
tools/testing/selftests/drivers/net/hw/devmem.py | 19 ++++++++++++++++++-
tools/testing/selftests/drivers/net/hw/ncdevmem.c | 11 ++++++++++-
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py
index 45c2d49d55b6..ee863e90d1e0 100755
--- a/tools/testing/selftests/drivers/net/hw/devmem.py
+++ b/tools/testing/selftests/drivers/net/hw/devmem.py
@@ -63,12 +63,29 @@ def check_tx_chunks(cfg) -> None:
ksft_eq(socat.stdout.strip(), "hello\nworld")
+def check_rx_hds(cfg) -> None:
+ """Test HDS splitting across payload sizes."""
+ require_devmem(cfg)
+
+ for size in [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]:
+ port = rand_port()
+ listen_cmd = f"{cfg.bin_local} -L -l -f {cfg.ifname} -s {cfg.addr} -p {port}"
+
+ with bkg(listen_cmd, exit_wait=True) as ncdevmem:
+ wait_port_listen(port)
+ cmd(f"dd if=/dev/zero bs={size} count=1 2>/dev/null | " +
+ f"socat -b {size} -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},nodelay",
+ host=cfg.remote, shell=True)
+
+ ksft_eq(ncdevmem.ret, 0, f"HDS failed for payload size {size}")
+
+
def main() -> None:
with NetDrvEpEnv(__file__) as cfg:
cfg.bin_local = path.abspath(path.dirname(__file__) + "/ncdevmem")
cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
- ksft_run([check_rx, check_tx, check_tx_chunks],
+ ksft_run([check_rx, check_tx, check_tx_chunks, check_rx_hds],
args=(cfg, ))
ksft_exit()
diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
index 3288ed04ce08..adc89591c834 100644
--- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c
+++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
@@ -97,6 +97,7 @@ static unsigned int ifindex;
static unsigned int dmabuf_id;
static uint32_t tx_dmabuf_id;
static int waittime_ms = 500;
+static bool fail_on_linear;
/* System state loaded by current_config_load() */
#define MAX_FLOWS 8
@@ -974,6 +975,11 @@ static int do_server(struct memory_buffer *mem)
"SCM_DEVMEM_LINEAR. dmabuf_cmsg->frag_size=%u\n",
dmabuf_cmsg->frag_size);
+ if (fail_on_linear) {
+ pr_err("received SCM_DEVMEM_LINEAR but --fail-on-linear (-L) set");
+ goto err_close_client;
+ }
+
continue;
}
@@ -1397,8 +1403,11 @@ int main(int argc, char *argv[])
int is_server = 0, opt;
int ret, err = 1;
- while ((opt = getopt(argc, argv, "ls:c:p:v:q:t:f:z:")) != -1) {
+ while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:")) != -1) {
switch (opt) {
+ case 'L':
+ fail_on_linear = true;
+ break;
case 'l':
is_server = 1;
break;
--
2.47.3