Commit 788e52e2 authored by Joe Damato's avatar Joe Damato Committed by Jakub Kicinski
Browse files

selftests: drv-net: Test queue xsk attribute



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: default avatarJoe Damato <jdamato@fastly.com>
Suggested-by: default avatarJakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20250214211255.14194-4-jdamato@fastly.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent df524c8f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
xdp_helper
+3 −0
Original line number Diff line number Diff line
# 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 \
+1 −0
Original line number Diff line number Diff line
@@ -4,3 +4,4 @@ CONFIG_CONFIGFS_FS=y
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_XDP_SOCKETS=y
+39 −3
Original line number Diff line number Diff line
@@ -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}-*')
@@ -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)
@@ -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()


+98 −0
Original line number Diff line number Diff line
// 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;
}