Skip to content

Commit

Permalink
selftests: drv-net: Test queue xsk attribute
Browse files Browse the repository at this point in the history
Test that queues which are used for AF_XDP have the xsk nest attribute.
The attribute is currently empty, but its existence means the AF_XDP is
being used for the queue. Enable CONFIG_XDP_SOCKETS for
selftests/drivers/net tests, as well.

Signed-off-by: Joe Damato <jdamato@fastly.com>
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20250214211255.14194-4-jdamato@fastly.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Joe Damato authored and Jakub Kicinski committed Feb 18, 2025
1 parent df524c8 commit 788e52e
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 3 deletions.
2 changes: 2 additions & 0 deletions tools/testing/selftests/drivers/net/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
xdp_helper
3 changes: 3 additions & 0 deletions tools/testing/selftests/drivers/net/Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += $(KHDR_INCLUDES)

TEST_INCLUDES := $(wildcard lib/py/*.py) \
$(wildcard lib/sh/*.sh) \
../../net/net_helper.sh \
../../net/lib.sh \

TEST_GEN_FILES := xdp_helper

TEST_PROGS := \
netcons_basic.sh \
netcons_fragmented_msg.sh \
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/drivers/net/config
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ CONFIG_CONFIGFS_FS=y
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_XDP_SOCKETS=y
42 changes: 39 additions & 3 deletions tools/testing/selftests/drivers/net/queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
# SPDX-License-Identifier: GPL-2.0

from lib.py import ksft_disruptive, ksft_exit, ksft_run
from lib.py import ksft_eq, ksft_raises, KsftSkipEx
from lib.py import ksft_eq, ksft_raises, KsftSkipEx, KsftFailEx
from lib.py import EthtoolFamily, NetdevFamily, NlError
from lib.py import NetDrvEnv
from lib.py import cmd, defer, ip
import errno
import glob

import os
import socket
import struct
import subprocess

def sys_get_queues(ifname, qtype='rx') -> int:
folders = glob.glob(f'/sys/class/net/{ifname}/queues/{qtype}-*')
Expand All @@ -21,6 +24,39 @@ def nl_get_queues(cfg, nl, qtype='rx'):
return len([q for q in queues if q['type'] == qtype])
return None

def check_xdp(cfg, nl, xdp_queue_id=0) -> None:
test_dir = os.path.dirname(os.path.realpath(__file__))
xdp = subprocess.Popen([f"{test_dir}/xdp_helper", f"{cfg.ifindex}", f"{xdp_queue_id}"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=1,
text=True)
defer(xdp.kill)

stdout, stderr = xdp.communicate(timeout=10)
rx = tx = False

if xdp.returncode == 255:
raise KsftSkipEx('AF_XDP unsupported')
elif xdp.returncode > 0:
raise KsftFailEx('unable to create AF_XDP socket')

queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True)
if not queues:
raise KsftSkipEx("Netlink reports no queues")

for q in queues:
if q['id'] == 0:
if q['type'] == 'rx':
rx = True
if q['type'] == 'tx':
tx = True

ksft_eq(q['xsk'], {})
else:
if 'xsk' in q:
_fail("Check failed: xsk attribute set.")

ksft_eq(rx, True)
ksft_eq(tx, True)

def get_queues(cfg, nl) -> None:
snl = NetdevFamily(recv_size=4096)
Expand Down Expand Up @@ -81,7 +117,7 @@ def check_down(cfg, nl) -> None:

def main() -> None:
with NetDrvEnv(__file__, queue_count=100) as cfg:
ksft_run([get_queues, addremove_queues, check_down], args=(cfg, NetdevFamily()))
ksft_run([get_queues, addremove_queues, check_down, check_xdp], args=(cfg, NetdevFamily()))
ksft_exit()


Expand Down
98 changes: 98 additions & 0 deletions tools/testing/selftests/drivers/net/xdp_helper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <linux/if_xdp.h>
#include <linux/if_link.h>
#include <net/if.h>
#include <inttypes.h>

#define UMEM_SZ (1U << 16)
#define NUM_DESC (UMEM_SZ / 2048)

/* this is a simple helper program that creates an XDP socket and does the
* minimum necessary to get bind() to succeed.
*
* this test program is not intended to actually process packets, but could be
* extended in the future if that is actually needed.
*
* it is used by queues.py to ensure the xsk netlinux attribute is set
* correctly.
*/
int main(int argc, char **argv)
{
struct xdp_umem_reg umem_reg = { 0 };
struct sockaddr_xdp sxdp = { 0 };
int num_desc = NUM_DESC;
void *umem_area;
int ifindex;
int sock_fd;
int queue;
char byte;

if (argc != 3) {
fprintf(stderr, "Usage: %s ifindex queue_id", argv[0]);
return 1;
}

sock_fd = socket(AF_XDP, SOCK_RAW, 0);
if (sock_fd < 0) {
perror("socket creation failed");
/* if the kernel doesn't support AF_XDP, let the test program
* know with -1. All other error paths return 1.
*/
if (errno == EAFNOSUPPORT)
return -1;
return 1;
}

ifindex = atoi(argv[1]);
queue = atoi(argv[2]);

umem_area = mmap(NULL, UMEM_SZ, PROT_READ | PROT_WRITE, MAP_PRIVATE |
MAP_ANONYMOUS, -1, 0);
if (umem_area == MAP_FAILED) {
perror("mmap failed");
return 1;
}

umem_reg.addr = (uintptr_t)umem_area;
umem_reg.len = UMEM_SZ;
umem_reg.chunk_size = 2048;
umem_reg.headroom = 0;

setsockopt(sock_fd, SOL_XDP, XDP_UMEM_REG, &umem_reg,
sizeof(umem_reg));
setsockopt(sock_fd, SOL_XDP, XDP_UMEM_FILL_RING, &num_desc,
sizeof(num_desc));
setsockopt(sock_fd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &num_desc,
sizeof(num_desc));
setsockopt(sock_fd, SOL_XDP, XDP_RX_RING, &num_desc, sizeof(num_desc));

sxdp.sxdp_family = AF_XDP;
sxdp.sxdp_ifindex = ifindex;
sxdp.sxdp_queue_id = queue;
sxdp.sxdp_flags = 0;

if (bind(sock_fd, (struct sockaddr *)&sxdp, sizeof(sxdp)) != 0) {
munmap(umem_area, UMEM_SZ);
perror("bind failed");
close(sock_fd);
return 1;
}

/* give the parent program some data when the socket is ready*/
fprintf(stdout, "%d\n", sock_fd);

/* parent program will write a byte to stdin when its ready for this
* helper to exit
*/
read(STDIN_FILENO, &byte, 1);

close(sock_fd);
return 0;
}

0 comments on commit 788e52e

Please sign in to comment.