Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 31 Dec 2018 11:17:58 +0000 (UTC)
From:      Vincenzo Maffione <vmaffione@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r342649 - in head: etc/mtree tests/sys tests/sys/netmap
Message-ID:  <201812311117.wBVBHwGO071847@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: vmaffione
Date: Mon Dec 31 11:17:58 2018
New Revision: 342649
URL: https://svnweb.freebsd.org/changeset/base/342649

Log:
  netmap: add suite of unit tests
  
  Import the unit tests from upstream (https://github.com/luigirizzo/netmap
  ba02539859d46d33), and make them ready for use with Kyua.
  There are currently 38 regression tests, which test the kernel control ABI
  exposed by netmap to userspace applications:
  
    1: test for port info get
    2-5: tests for basic port registration
    6-9: tests for VALE
    10-11: tests for getting netmap allocator info
    12-15: tests for netmap pipes
    16: test on polling mode
    17-18: tests on options
    19-27: tests for sync-kloop subsystem
    28-39: tests for null ports
    31-38: tests for the legacy NIOCREGIF registers
  
  Reviewed by:	ngie
  MFC after:	1 week
  Differential Revision:	https://reviews.freebsd.org/D18490

Added:
  head/tests/sys/netmap/
  head/tests/sys/netmap/Makefile   (contents, props changed)
  head/tests/sys/netmap/ctrl-api-test.c   (contents, props changed)
Modified:
  head/etc/mtree/BSD.tests.dist
  head/tests/sys/Makefile

Modified: head/etc/mtree/BSD.tests.dist
==============================================================================
--- head/etc/mtree/BSD.tests.dist	Mon Dec 31 11:10:02 2018	(r342648)
+++ head/etc/mtree/BSD.tests.dist	Mon Dec 31 11:17:58 2018	(r342649)
@@ -760,6 +760,8 @@
             tunnel
             ..
         ..
+    netmap
+    ..
         netpfil
             pf
                 ioctl

Modified: head/tests/sys/Makefile
==============================================================================
--- head/tests/sys/Makefile	Mon Dec 31 11:10:02 2018	(r342648)
+++ head/tests/sys/Makefile	Mon Dec 31 11:17:58 2018	(r342649)
@@ -20,6 +20,7 @@ TESTS_SUBDIRS+=		mac
 TESTS_SUBDIRS+=		mqueue
 TESTS_SUBDIRS+=		netinet
 TESTS_SUBDIRS+=		netipsec
+TESTS_SUBDIRS+=		netmap
 TESTS_SUBDIRS+=		netpfil
 TESTS_SUBDIRS+=		opencrypto
 TESTS_SUBDIRS+=		posixshm

Added: head/tests/sys/netmap/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tests/sys/netmap/Makefile	Mon Dec 31 11:17:58 2018	(r342649)
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PACKAGE=	tests
+
+TESTSDIR=	${TESTSBASE}/sys/netmap
+TEST_METADATA+=	required_user="root"
+TEST_METADATA+=	is_exclusive=true
+
+LDFLAGS+= 	-lpthread
+PLAIN_TESTS_C+=	ctrl-api-test
+
+WARNS?=		6
+
+.include <bsd.test.mk>

Added: head/tests/sys/netmap/ctrl-api-test.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tests/sys/netmap/ctrl-api-test.c	Mon Dec 31 11:17:58 2018	(r342649)
@@ -0,0 +1,1905 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2018 Vincenzo Maffione
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <net/netmap.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#ifdef __linux__
+#include <sys/eventfd.h>
+#else
+static int
+eventfd(int x __unused, int y __unused)
+{
+	errno = ENODEV;
+	return -1;
+}
+#endif /* __linux__ */
+
+static int
+exec_command(int argc, const char *const argv[])
+{
+	pid_t child_pid;
+	pid_t wret;
+	int child_status;
+	int i;
+
+	printf("Executing command: ");
+	for (i = 0; i < argc - 1; i++) {
+		if (!argv[i]) {
+			/* Invalid argument. */
+			return -1;
+		}
+		if (i > 0) {
+			putchar(' ');
+		}
+		printf("%s", argv[i]);
+	}
+	putchar('\n');
+
+	child_pid = fork();
+	if (child_pid == 0) {
+		char **av;
+
+		/* Child process. Redirect stdin, stdout
+		 * and stderr. */
+		close(0);
+		close(1);
+		close(2);
+		if (open("/dev/null", O_RDONLY) < 0 ||
+			open("/dev/null", O_RDONLY) < 0 ||
+			open("/dev/null", O_RDONLY) < 0) {
+			return -1;
+		}
+
+		/* Make a copy of the arguments, passing them to execvp. */
+		av = calloc(argc, sizeof(av[0]));
+		if (!av) {
+			exit(EXIT_FAILURE);
+		}
+		for (i = 0; i < argc - 1; i++) {
+			av[i] = strdup(argv[i]);
+			if (!av[i]) {
+				exit(EXIT_FAILURE);
+			}
+		}
+		execvp(av[0], av);
+		perror("execvp()");
+		exit(EXIT_FAILURE);
+	}
+
+	wret = waitpid(child_pid, &child_status, 0);
+	if (wret < 0) {
+		fprintf(stderr, "waitpid() failed: %s\n", strerror(errno));
+		return wret;
+	}
+	if (WIFEXITED(child_status)) {
+		return WEXITSTATUS(child_status);
+	}
+
+	return -1;
+}
+
+
+#define THRET_SUCCESS	((void *)128)
+#define THRET_FAILURE	((void *)0)
+
+struct TestContext {
+	char ifname[128];
+	char bdgname[64];
+	uint32_t nr_tx_slots;   /* slots in tx rings */
+	uint32_t nr_rx_slots;   /* slots in rx rings */
+	uint16_t nr_tx_rings;   /* number of tx rings */
+	uint16_t nr_rx_rings;   /* number of rx rings */
+	uint16_t nr_mem_id;     /* id of the memory allocator */
+	uint16_t nr_ringid;     /* ring(s) we care about */
+	uint32_t nr_mode;       /* specify NR_REG_* modes */
+	uint32_t nr_extra_bufs; /* number of requested extra buffers */
+	uint64_t nr_flags;      /* additional flags (see below) */
+	uint32_t nr_hdr_len; /* for PORT_HDR_SET and PORT_HDR_GET */
+	uint32_t nr_first_cpu_id;     /* vale polling */
+	uint32_t nr_num_polling_cpus; /* vale polling */
+	int fd; /* netmap file descriptor */
+
+	void *csb;                    /* CSB entries (atok and ktoa) */
+	struct nmreq_option *nr_opt;  /* list of options */
+	sem_t *sem;	/* for thread synchronization */
+	struct nmport_d *nmport;      /* nmport descriptor from libnetmap */
+};
+
+static struct TestContext ctx_;
+
+typedef int (*testfunc_t)(struct TestContext *ctx);
+
+static void
+nmreq_hdr_init(struct nmreq_header *hdr, const char *ifname)
+{
+	memset(hdr, 0, sizeof(*hdr));
+	hdr->nr_version = NETMAP_API;
+	strncpy(hdr->nr_name, ifname, sizeof(hdr->nr_name) - 1);
+}
+
+/* Single NETMAP_REQ_PORT_INFO_GET. */
+static int
+port_info_get(struct TestContext *ctx)
+{
+	struct nmreq_port_info_get req;
+	struct nmreq_header hdr;
+	int success;
+	int ret;
+
+	printf("Testing NETMAP_REQ_PORT_INFO_GET on '%s'\n", ctx->ifname);
+
+	nmreq_hdr_init(&hdr, ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id = ctx->nr_mem_id;
+	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_INFO_GET)");
+		return ret;
+	}
+	printf("nr_memsize %lu\n", req.nr_memsize);
+	printf("nr_tx_slots %u\n", req.nr_tx_slots);
+	printf("nr_rx_slots %u\n", req.nr_rx_slots);
+	printf("nr_tx_rings %u\n", req.nr_tx_rings);
+	printf("nr_rx_rings %u\n", req.nr_rx_rings);
+	printf("nr_mem_id %u\n", req.nr_mem_id);
+
+	success = req.nr_memsize && req.nr_tx_slots && req.nr_rx_slots &&
+	          req.nr_tx_rings && req.nr_rx_rings && req.nr_tx_rings;
+	if (!success) {
+		return -1;
+	}
+
+	/* Write back results to the context structure. */
+	ctx->nr_tx_slots = req.nr_tx_slots;
+	ctx->nr_rx_slots = req.nr_rx_slots;
+	ctx->nr_tx_rings = req.nr_tx_rings;
+	ctx->nr_rx_rings = req.nr_rx_rings;
+	ctx->nr_mem_id   = req.nr_mem_id;
+
+	return 0;
+}
+
+/* Single NETMAP_REQ_REGISTER, no use. */
+static int
+port_register(struct TestContext *ctx)
+{
+	struct nmreq_register req;
+	struct nmreq_header hdr;
+	int success;
+	int ret;
+
+	printf("Testing NETMAP_REQ_REGISTER(mode=%d,ringid=%d,"
+	       "flags=0x%lx) on '%s'\n",
+	       ctx->nr_mode, ctx->nr_ringid, ctx->nr_flags, ctx->ifname);
+
+	nmreq_hdr_init(&hdr, ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_REGISTER;
+	hdr.nr_body    = (uintptr_t)&req;
+	hdr.nr_options = (uintptr_t)ctx->nr_opt;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id     = ctx->nr_mem_id;
+	req.nr_mode       = ctx->nr_mode;
+	req.nr_ringid     = ctx->nr_ringid;
+	req.nr_flags      = ctx->nr_flags;
+	req.nr_tx_slots   = ctx->nr_tx_slots;
+	req.nr_rx_slots   = ctx->nr_rx_slots;
+	req.nr_tx_rings   = ctx->nr_tx_rings;
+	req.nr_rx_rings   = ctx->nr_rx_rings;
+	req.nr_extra_bufs = ctx->nr_extra_bufs;
+	ret               = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, REGISTER)");
+		return ret;
+	}
+	printf("nr_offset 0x%lx\n", req.nr_offset);
+	printf("nr_memsize %lu\n", req.nr_memsize);
+	printf("nr_tx_slots %u\n", req.nr_tx_slots);
+	printf("nr_rx_slots %u\n", req.nr_rx_slots);
+	printf("nr_tx_rings %u\n", req.nr_tx_rings);
+	printf("nr_rx_rings %u\n", req.nr_rx_rings);
+	printf("nr_mem_id %u\n", req.nr_mem_id);
+	printf("nr_extra_bufs %u\n", req.nr_extra_bufs);
+
+	success = req.nr_memsize && (ctx->nr_mode == req.nr_mode) &&
+	                       (ctx->nr_ringid == req.nr_ringid) &&
+	                       (ctx->nr_flags == req.nr_flags) &&
+	                       ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
+	                        (ctx->nr_tx_slots == req.nr_tx_slots)) &&
+	                       ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
+	                        (ctx->nr_rx_slots == req.nr_rx_slots)) &&
+	                       ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
+	                        (ctx->nr_tx_rings == req.nr_tx_rings)) &&
+	                       ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
+	                        (ctx->nr_rx_rings == req.nr_rx_rings)) &&
+	                       ((!ctx->nr_mem_id && req.nr_mem_id) ||
+	                        (ctx->nr_mem_id == req.nr_mem_id)) &&
+	                       (ctx->nr_extra_bufs == req.nr_extra_bufs);
+	if (!success) {
+		return -1;
+	}
+
+	/* Write back results to the context structure.*/
+	ctx->nr_tx_slots   = req.nr_tx_slots;
+	ctx->nr_rx_slots   = req.nr_rx_slots;
+	ctx->nr_tx_rings   = req.nr_tx_rings;
+	ctx->nr_rx_rings   = req.nr_rx_rings;
+	ctx->nr_mem_id     = req.nr_mem_id;
+	ctx->nr_extra_bufs = req.nr_extra_bufs;
+
+	return 0;
+}
+
+static int
+niocregif(struct TestContext *ctx, int netmap_api)
+{
+	struct nmreq req;
+	int success;
+	int ret;
+
+	printf("Testing legacy NIOCREGIF on '%s'\n", ctx->ifname);
+
+	memset(&req, 0, sizeof(req));
+	memcpy(req.nr_name, ctx->ifname, sizeof(req.nr_name));
+	req.nr_name[sizeof(req.nr_name) - 1] = '\0';
+	req.nr_version = netmap_api;
+	req.nr_ringid     = ctx->nr_ringid;
+	req.nr_flags      = ctx->nr_mode | ctx->nr_flags;
+	req.nr_tx_slots   = ctx->nr_tx_slots;
+	req.nr_rx_slots   = ctx->nr_rx_slots;
+	req.nr_tx_rings   = ctx->nr_tx_rings;
+	req.nr_rx_rings   = ctx->nr_rx_rings;
+	req.nr_arg2     = ctx->nr_mem_id;
+	req.nr_arg3 = ctx->nr_extra_bufs;
+
+	ret = ioctl(ctx->fd, NIOCREGIF, &req);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCREGIF)");
+		return ret;
+	}
+
+	printf("nr_offset 0x%x\n", req.nr_offset);
+	printf("nr_memsize  %u\n", req.nr_memsize);
+	printf("nr_tx_slots %u\n", req.nr_tx_slots);
+	printf("nr_rx_slots %u\n", req.nr_rx_slots);
+	printf("nr_tx_rings %u\n", req.nr_tx_rings);
+	printf("nr_rx_rings %u\n", req.nr_rx_rings);
+	printf("nr_version  %d\n", req.nr_version);
+	printf("nr_ringid   %x\n", req.nr_ringid);
+	printf("nr_flags    %x\n", req.nr_flags);
+	printf("nr_arg2     %u\n", req.nr_arg2);
+	printf("nr_arg3     %u\n", req.nr_arg3);
+
+	success = req.nr_memsize &&
+	       (ctx->nr_ringid == req.nr_ringid) &&
+	       ((ctx->nr_mode | ctx->nr_flags) == req.nr_flags) &&
+	       ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
+		(ctx->nr_tx_slots == req.nr_tx_slots)) &&
+	       ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
+		(ctx->nr_rx_slots == req.nr_rx_slots)) &&
+	       ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
+		(ctx->nr_tx_rings == req.nr_tx_rings)) &&
+	       ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
+		(ctx->nr_rx_rings == req.nr_rx_rings)) &&
+	       ((!ctx->nr_mem_id && req.nr_arg2) ||
+		(ctx->nr_mem_id == req.nr_arg2)) &&
+	       (ctx->nr_extra_bufs == req.nr_arg3);
+	if (!success) {
+		return -1;
+	}
+
+	/* Write back results to the context structure.*/
+	ctx->nr_tx_slots   = req.nr_tx_slots;
+	ctx->nr_rx_slots   = req.nr_rx_slots;
+	ctx->nr_tx_rings   = req.nr_tx_rings;
+	ctx->nr_rx_rings   = req.nr_rx_rings;
+	ctx->nr_mem_id     = req.nr_arg2;
+	ctx->nr_extra_bufs = req.nr_arg3;
+
+	return ret;
+}
+
+/* The 11 ABI is the one right before the introduction of the new NIOCCTRL
+ * ABI. The 11 ABI is useful to perform tests with legacy applications
+ * (which use the 11 ABI) and new kernel (which uses 12, or higher). */
+#define NETMAP_API_NIOCREGIF	11
+
+static int
+legacy_regif_default(struct TestContext *ctx)
+{
+	return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_all_nic(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	return niocregif(ctx, NETMAP_API);
+}
+
+static int
+legacy_regif_12(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	return niocregif(ctx, NETMAP_API_NIOCREGIF+1);
+}
+
+static int
+legacy_regif_sw(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_SW;
+	return niocregif(ctx,  NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_future(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_NIC_SW;
+	/* Test forward compatibility for the legacy ABI. This means
+	 * using an older kernel (with ABI 12 or higher) and a newer
+	 * application (with ABI greater than NETMAP_API). */
+	return niocregif(ctx, NETMAP_API+2);
+}
+
+static int
+legacy_regif_extra_bufs(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_extra_bufs = 20;	/* arbitrary number of extra bufs */
+	return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_extra_bufs_pipe(struct TestContext *ctx)
+{
+	strncat(ctx->ifname, "{pipeexbuf", sizeof(ctx->ifname));
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_extra_bufs = 58;	/* arbitrary number of extra bufs */
+
+	return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_extra_bufs_pipe_vale(struct TestContext *ctx)
+{
+	strncpy(ctx->ifname, "valeX1:Y4", sizeof(ctx->ifname));
+	return legacy_regif_extra_bufs_pipe(ctx);
+}
+
+/* Only valid after a successful port_register(). */
+static int
+num_registered_rings(struct TestContext *ctx)
+{
+	if (ctx->nr_flags & NR_TX_RINGS_ONLY) {
+		return ctx->nr_tx_rings;
+	}
+	if (ctx->nr_flags & NR_RX_RINGS_ONLY) {
+		return ctx->nr_rx_rings;
+	}
+
+	return ctx->nr_tx_rings + ctx->nr_rx_rings;
+}
+
+static int
+port_register_hwall_host(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_NIC_SW;
+	return port_register(ctx);
+}
+
+static int
+port_register_host(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_SW;
+	return port_register(ctx);
+}
+
+static int
+port_register_hwall(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	return port_register(ctx);
+}
+
+static int
+port_register_single_ring_couple(struct TestContext *ctx)
+{
+	ctx->nr_mode   = NR_REG_ONE_NIC;
+	ctx->nr_ringid = 0;
+	return port_register(ctx);
+}
+
+static int
+port_register_hwall_tx(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_flags |= NR_TX_RINGS_ONLY;
+	return port_register(ctx);
+}
+
+static int
+port_register_hwall_rx(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_flags |= NR_RX_RINGS_ONLY;
+	return port_register(ctx);
+}
+
+/* NETMAP_REQ_VALE_ATTACH */
+static int
+vale_attach(struct TestContext *ctx)
+{
+	struct nmreq_vale_attach req;
+	struct nmreq_header hdr;
+	char vpname[sizeof(ctx->bdgname) + 1 + sizeof(ctx->ifname)];
+	int ret;
+
+	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname);
+
+	printf("Testing NETMAP_REQ_VALE_ATTACH on '%s'\n", vpname);
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.reg.nr_mem_id = ctx->nr_mem_id;
+	if (ctx->nr_mode == 0) {
+		ctx->nr_mode = NR_REG_ALL_NIC; /* default */
+	}
+	req.reg.nr_mode = ctx->nr_mode;
+	ret             = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_ATTACH)");
+		return ret;
+	}
+	printf("nr_mem_id %u\n", req.reg.nr_mem_id);
+
+	return ((!ctx->nr_mem_id && req.reg.nr_mem_id > 1) ||
+	        (ctx->nr_mem_id == req.reg.nr_mem_id)) &&
+	                       (ctx->nr_flags == req.reg.nr_flags)
+	               ? 0
+	               : -1;
+}
+
+/* NETMAP_REQ_VALE_DETACH */
+static int
+vale_detach(struct TestContext *ctx)
+{
+	struct nmreq_header hdr;
+	struct nmreq_vale_detach req;
+	char vpname[256];
+	int ret;
+
+	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname);
+
+	printf("Testing NETMAP_REQ_VALE_DETACH on '%s'\n", vpname);
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_DETACH;
+	hdr.nr_body    = (uintptr_t)&req;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_DETACH)");
+		return ret;
+	}
+
+	return 0;
+}
+
+/* First NETMAP_REQ_VALE_ATTACH, then NETMAP_REQ_VALE_DETACH. */
+static int
+vale_attach_detach(struct TestContext *ctx)
+{
+	int ret;
+
+	if ((ret = vale_attach(ctx)) != 0) {
+		return ret;
+	}
+
+	return vale_detach(ctx);
+}
+
+static int
+vale_attach_detach_host_rings(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_NIC_SW;
+	return vale_attach_detach(ctx);
+}
+
+/* First NETMAP_REQ_PORT_HDR_SET and the NETMAP_REQ_PORT_HDR_GET
+ * to check that we get the same value. */
+static int
+port_hdr_set_and_get(struct TestContext *ctx)
+{
+	struct nmreq_port_hdr req;
+	struct nmreq_header hdr;
+	int ret;
+
+	printf("Testing NETMAP_REQ_PORT_HDR_SET on '%s'\n", ctx->ifname);
+
+	nmreq_hdr_init(&hdr, ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_hdr_len = ctx->nr_hdr_len;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
+		return ret;
+	}
+
+	if (req.nr_hdr_len != ctx->nr_hdr_len) {
+		return -1;
+	}
+
+	printf("Testing NETMAP_REQ_PORT_HDR_GET on '%s'\n", ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+	req.nr_hdr_len = 0;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
+		return ret;
+	}
+	printf("nr_hdr_len %u\n", req.nr_hdr_len);
+
+	return (req.nr_hdr_len == ctx->nr_hdr_len) ? 0 : -1;
+}
+
+/*
+ * Possible lengths for the VirtIO network header, as specified by
+ * the standard:
+ *    http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html
+ */
+#define VIRTIO_NET_HDR_LEN				10
+#define VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS	12
+
+static int
+vale_ephemeral_port_hdr_manipulation(struct TestContext *ctx)
+{
+	int ret;
+
+	strncpy(ctx->ifname, "vale:eph0", sizeof(ctx->ifname));
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	if ((ret = port_register(ctx))) {
+		return ret;
+	}
+	/* Try to set and get all the acceptable values. */
+	ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS;
+	if ((ret = port_hdr_set_and_get(ctx))) {
+		return ret;
+	}
+	ctx->nr_hdr_len = 0;
+	if ((ret = port_hdr_set_and_get(ctx))) {
+		return ret;
+	}
+	ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN;
+	if ((ret = port_hdr_set_and_get(ctx))) {
+		return ret;
+	}
+	return 0;
+}
+
+static int
+vale_persistent_port(struct TestContext *ctx)
+{
+	struct nmreq_vale_newif req;
+	struct nmreq_header hdr;
+	int result;
+	int ret;
+
+	strncpy(ctx->ifname, "per4", sizeof(ctx->ifname));
+
+	printf("Testing NETMAP_REQ_VALE_NEWIF on '%s'\n", ctx->ifname);
+
+	nmreq_hdr_init(&hdr, ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id   = ctx->nr_mem_id;
+	req.nr_tx_slots = ctx->nr_tx_slots;
+	req.nr_rx_slots = ctx->nr_rx_slots;
+	req.nr_tx_rings = ctx->nr_tx_rings;
+	req.nr_rx_rings = ctx->nr_rx_rings;
+	ret             = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
+		return ret;
+	}
+
+	/* Attach the persistent VALE port to a switch and then detach. */
+	result = vale_attach_detach(ctx);
+
+	printf("Testing NETMAP_REQ_VALE_DELIF on '%s'\n", ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_DELIF;
+	hdr.nr_body    = (uintptr_t)NULL;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
+		if (result == 0) {
+			result = ret;
+		}
+	}
+
+	return result;
+}
+
+/* Single NETMAP_REQ_POOLS_INFO_GET. */
+static int
+pools_info_get(struct TestContext *ctx)
+{
+	struct nmreq_pools_info req;
+	struct nmreq_header hdr;
+	int ret;
+
+	printf("Testing NETMAP_REQ_POOLS_INFO_GET on '%s'\n", ctx->ifname);
+
+	nmreq_hdr_init(&hdr, ctx->ifname);
+	hdr.nr_reqtype = NETMAP_REQ_POOLS_INFO_GET;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id = ctx->nr_mem_id;
+	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, POOLS_INFO_GET)");
+		return ret;
+	}
+	printf("nr_memsize %lu\n", req.nr_memsize);
+	printf("nr_mem_id %u\n", req.nr_mem_id);
+	printf("nr_if_pool_offset 0x%lx\n", req.nr_if_pool_offset);
+	printf("nr_if_pool_objtotal %u\n", req.nr_if_pool_objtotal);
+	printf("nr_if_pool_objsize %u\n", req.nr_if_pool_objsize);
+	printf("nr_ring_pool_offset 0x%lx\n", req.nr_if_pool_offset);
+	printf("nr_ring_pool_objtotal %u\n", req.nr_ring_pool_objtotal);
+	printf("nr_ring_pool_objsize %u\n", req.nr_ring_pool_objsize);
+	printf("nr_buf_pool_offset 0x%lx\n", req.nr_buf_pool_offset);
+	printf("nr_buf_pool_objtotal %u\n", req.nr_buf_pool_objtotal);
+	printf("nr_buf_pool_objsize %u\n", req.nr_buf_pool_objsize);
+
+	return req.nr_memsize && req.nr_if_pool_objtotal &&
+	                       req.nr_if_pool_objsize &&
+	                       req.nr_ring_pool_objtotal &&
+	                       req.nr_ring_pool_objsize &&
+	                       req.nr_buf_pool_objtotal &&
+	                       req.nr_buf_pool_objsize
+	               ? 0
+	               : -1;
+}
+
+static int
+pools_info_get_and_register(struct TestContext *ctx)
+{
+	int ret;
+
+	/* Check that we can get pools info before we register
+	 * a netmap interface. */
+	ret = pools_info_get(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ctx->nr_mode = NR_REG_ONE_NIC;
+	ret          = port_register(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	ctx->nr_mem_id = 1;
+
+	/* Check that we can get pools info also after we register. */
+	return pools_info_get(ctx);
+}
+
+static int
+pools_info_get_empty_ifname(struct TestContext *ctx)
+{
+	strncpy(ctx->ifname, "", sizeof(ctx->ifname));
+	return pools_info_get(ctx) != 0 ? 0 : -1;
+}
+
+static int
+pipe_master(struct TestContext *ctx)
+{
+	strncat(ctx->ifname, "{pipeid1", sizeof(ctx->ifname));
+	ctx->nr_mode = NR_REG_NIC_SW;
+
+	if (port_register(ctx) == 0) {
+		printf("pipes should not accept NR_REG_NIC_SW\n");
+		return -1;
+	}
+	ctx->nr_mode = NR_REG_ALL_NIC;
+
+	return port_register(ctx);
+}
+
+static int
+pipe_slave(struct TestContext *ctx)
+{
+	strncat(ctx->ifname, "}pipeid2", sizeof(ctx->ifname));
+	ctx->nr_mode = NR_REG_ALL_NIC;
+
+	return port_register(ctx);
+}
+
+/* Test PORT_INFO_GET and POOLS_INFO_GET on a pipe. This is useful to test the
+ * registration request used internall by netmap. */
+static int
+pipe_port_info_get(struct TestContext *ctx)
+{
+	strncat(ctx->ifname, "}pipeid3", sizeof(ctx->ifname));
+
+	return port_info_get(ctx);
+}
+
+static int
+pipe_pools_info_get(struct TestContext *ctx)
+{
+	strncat(ctx->ifname, "{xid", sizeof(ctx->ifname));
+
+	return pools_info_get(ctx);
+}
+
+/* NETMAP_REQ_VALE_POLLING_ENABLE */
+static int
+vale_polling_enable(struct TestContext *ctx)
+{
+	struct nmreq_vale_polling req;
+	struct nmreq_header hdr;
+	char vpname[256];
+	int ret;
+
+	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname);
+	printf("Testing NETMAP_REQ_VALE_POLLING_ENABLE on '%s'\n", vpname);
+
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mode             = ctx->nr_mode;
+	req.nr_first_cpu_id     = ctx->nr_first_cpu_id;
+	req.nr_num_polling_cpus = ctx->nr_num_polling_cpus;
+	ret                     = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_ENABLE)");
+		return ret;
+	}
+
+	return (req.nr_mode == ctx->nr_mode &&
+	        req.nr_first_cpu_id == ctx->nr_first_cpu_id &&
+	        req.nr_num_polling_cpus == ctx->nr_num_polling_cpus)
+	               ? 0
+	               : -1;
+}
+
+/* NETMAP_REQ_VALE_POLLING_DISABLE */
+static int
+vale_polling_disable(struct TestContext *ctx)
+{
+	struct nmreq_vale_polling req;
+	struct nmreq_header hdr;
+	char vpname[256];
+	int ret;
+
+	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname);
+	printf("Testing NETMAP_REQ_VALE_POLLING_DISABLE on '%s'\n", vpname);
+
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_DISABLE)");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+vale_polling_enable_disable(struct TestContext *ctx)
+{
+	int ret = 0;
+
+	if ((ret = vale_attach(ctx)) != 0) {
+		return ret;
+	}
+
+	ctx->nr_mode             = NETMAP_POLLING_MODE_SINGLE_CPU;
+	ctx->nr_num_polling_cpus = 1;
+	ctx->nr_first_cpu_id     = 0;
+	if ((ret = vale_polling_enable(ctx))) {
+		vale_detach(ctx);
+#ifdef __FreeBSD__
+		/* NETMAP_REQ_VALE_POLLING_DISABLE is disabled on FreeBSD,
+		 * because it is currently broken. We are happy to see that
+		 * it fails. */
+		return 0;
+#endif
+		return ret;
+	}
+
+	if ((ret = vale_polling_disable(ctx))) {
+		vale_detach(ctx);
+		return ret;
+	}
+
+	return vale_detach(ctx);
+}
+
+static void
+push_option(struct nmreq_option *opt, struct TestContext *ctx)
+{
+	opt->nro_next = (uintptr_t)ctx->nr_opt;
+	ctx->nr_opt   = opt;
+}
+
+static void
+clear_options(struct TestContext *ctx)
+{
+	ctx->nr_opt = NULL;
+}
+
+static int
+checkoption(struct nmreq_option *opt, struct nmreq_option *exp)
+{
+	if (opt->nro_next != exp->nro_next) {
+		printf("nro_next %p expected %p\n",
+		       (void *)(uintptr_t)opt->nro_next,
+		       (void *)(uintptr_t)exp->nro_next);
+		return -1;
+	}
+	if (opt->nro_reqtype != exp->nro_reqtype) {
+		printf("nro_reqtype %u expected %u\n", opt->nro_reqtype,
+		       exp->nro_reqtype);
+		return -1;
+	}
+	if (opt->nro_status != exp->nro_status) {
+		printf("nro_status %u expected %u\n", opt->nro_status,
+		       exp->nro_status);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+unsupported_option(struct TestContext *ctx)
+{
+	struct nmreq_option opt, save;
+
+	printf("Testing unsupported option on %s\n", ctx->ifname);
+
+	memset(&opt, 0, sizeof(opt));
+	opt.nro_reqtype = 1234;
+	push_option(&opt, ctx);
+	save = opt;
+
+	if (port_register_hwall(ctx) >= 0)
+		return -1;
+
+	clear_options(ctx);
+	save.nro_status = EOPNOTSUPP;
+	return checkoption(&opt, &save);
+}
+
+static int
+infinite_options(struct TestContext *ctx)
+{
+	struct nmreq_option opt;
+
+	printf("Testing infinite list of options on %s\n", ctx->ifname);
+
+	opt.nro_reqtype = 1234;
+	push_option(&opt, ctx);
+	opt.nro_next = (uintptr_t)&opt;
+	if (port_register_hwall(ctx) >= 0)
+		return -1;
+	clear_options(ctx);
+	return (errno == EMSGSIZE ? 0 : -1);
+}
+
+#ifdef CONFIG_NETMAP_EXTMEM
+int
+change_param(const char *pname, unsigned long newv, unsigned long *poldv)
+{
+#ifdef __linux__
+	char param[256] = "/sys/module/netmap/parameters/";
+	unsigned long oldv;
+	FILE *f;
+
+	strncat(param, pname, sizeof(param) - 1);
+
+	f = fopen(param, "r+");
+	if (f == NULL) {
+		perror(param);
+		return -1;
+	}
+	if (fscanf(f, "%ld", &oldv) != 1) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201812311117.wBVBHwGO071847>