1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (C) 2018 Vincenzo Maffione
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *   1. Redistributions of source code must retain the above copyright
10 *      notice, this list of conditions and the following disclaimer.
11 *   2. Redistributions in binary form must reproduce the above copyright
12 *      notice, this list of conditions and the following disclaimer in the
13 *      documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#include <sys/ioctl.h>
31#include <sys/mman.h>
32#include <sys/wait.h>
33
34#include <assert.h>
35#include <ctype.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <inttypes.h>
39#include <net/if.h>
40#include <net/netmap.h>
41#include <pthread.h>
42#include <semaphore.h>
43#include <stdint.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48#include <unistd.h>
49#include <signal.h>
50
51#ifdef __FreeBSD__
52#include "freebsd_test_suite/macros.h"
53
54static int
55eventfd(int x __unused, int y __unused)
56{
57	errno = ENODEV;
58	return -1;
59}
60#else /* __linux__ */
61#include <sys/eventfd.h>
62#endif
63
64static int
65exec_command(int argc, const char *const argv[])
66{
67	pid_t child_pid;
68	pid_t wret;
69	int child_status;
70	int i;
71
72	printf("Executing command: ");
73	for (i = 0; i < argc - 1; i++) {
74		if (!argv[i]) {
75			/* Invalid argument. */
76			return -1;
77		}
78		if (i > 0) {
79			putchar(' ');
80		}
81		printf("%s", argv[i]);
82	}
83	putchar('\n');
84
85	child_pid = fork();
86	if (child_pid == 0) {
87		char **av;
88		int fds[3];
89
90		/* Child process. Redirect stdin, stdout
91		 * and stderr. */
92		for (i = 0; i < 3; i++) {
93			close(i);
94			fds[i] = open("/dev/null", O_RDONLY);
95			if (fds[i] < 0) {
96				for (i--; i >= 0; i--) {
97					close(fds[i]);
98				}
99				return -1;
100			}
101		}
102
103		/* Make a copy of the arguments, passing them to execvp. */
104		av = calloc(argc, sizeof(av[0]));
105		if (!av) {
106			exit(EXIT_FAILURE);
107		}
108		for (i = 0; i < argc - 1; i++) {
109			av[i] = strdup(argv[i]);
110			if (!av[i]) {
111				exit(EXIT_FAILURE);
112			}
113		}
114		execvp(av[0], av);
115		perror("execvp()");
116		exit(EXIT_FAILURE);
117	}
118
119	wret = waitpid(child_pid, &child_status, 0);
120	if (wret < 0) {
121		fprintf(stderr, "waitpid() failed: %s\n", strerror(errno));
122		return wret;
123	}
124	if (WIFEXITED(child_status)) {
125		return WEXITSTATUS(child_status);
126	}
127
128	return -1;
129}
130
131
132#define THRET_SUCCESS	((void *)128)
133#define THRET_FAILURE	((void *)0)
134
135struct TestContext {
136	char ifname[64];
137	char ifname_ext[128];
138	char bdgname[64];
139	uint32_t nr_tx_slots;   /* slots in tx rings */
140	uint32_t nr_rx_slots;   /* slots in rx rings */
141	uint16_t nr_tx_rings;   /* number of tx rings */
142	uint16_t nr_rx_rings;   /* number of rx rings */
143	uint16_t nr_host_tx_rings;   /* number of host tx rings */
144	uint16_t nr_host_rx_rings;   /* number of host rx rings */
145	uint16_t nr_mem_id;     /* id of the memory allocator */
146	uint16_t nr_ringid;     /* ring(s) we care about */
147	uint32_t nr_mode;       /* specify NR_REG_* modes */
148	uint32_t nr_extra_bufs; /* number of requested extra buffers */
149	uint64_t nr_flags;      /* additional flags (see below) */
150	uint32_t nr_hdr_len; /* for PORT_HDR_SET and PORT_HDR_GET */
151	uint32_t nr_first_cpu_id;     /* vale polling */
152	uint32_t nr_num_polling_cpus; /* vale polling */
153	uint32_t sync_kloop_mode; /* sync-kloop */
154	int fd; /* netmap file descriptor */
155
156	void *csb;                    /* CSB entries (atok and ktoa) */
157	struct nmreq_option *nr_opt;  /* list of options */
158	sem_t *sem;	/* for thread synchronization */
159};
160
161static struct TestContext ctx_;
162
163typedef int (*testfunc_t)(struct TestContext *ctx);
164
165static void
166nmreq_hdr_init(struct nmreq_header *hdr, const char *ifname)
167{
168	memset(hdr, 0, sizeof(*hdr));
169	hdr->nr_version = NETMAP_API;
170	strncpy(hdr->nr_name, ifname, sizeof(hdr->nr_name) - 1);
171}
172
173/* Single NETMAP_REQ_PORT_INFO_GET. */
174static int
175port_info_get(struct TestContext *ctx)
176{
177	struct nmreq_port_info_get req;
178	struct nmreq_header hdr;
179	int success;
180	int ret;
181
182	printf("Testing NETMAP_REQ_PORT_INFO_GET on '%s'\n", ctx->ifname_ext);
183
184	nmreq_hdr_init(&hdr, ctx->ifname_ext);
185	hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
186	hdr.nr_body    = (uintptr_t)&req;
187	memset(&req, 0, sizeof(req));
188	req.nr_mem_id = ctx->nr_mem_id;
189	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
190	if (ret != 0) {
191		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_INFO_GET)");
192		return ret;
193	}
194	printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
195	printf("nr_tx_slots %u\n", req.nr_tx_slots);
196	printf("nr_rx_slots %u\n", req.nr_rx_slots);
197	printf("nr_tx_rings %u\n", req.nr_tx_rings);
198	printf("nr_rx_rings %u\n", req.nr_rx_rings);
199	printf("nr_mem_id %u\n", req.nr_mem_id);
200
201	success = req.nr_memsize && req.nr_tx_slots && req.nr_rx_slots &&
202	          req.nr_tx_rings && req.nr_rx_rings && req.nr_tx_rings;
203	if (!success) {
204		return -1;
205	}
206
207	/* Write back results to the context structure. */
208	ctx->nr_tx_slots = req.nr_tx_slots;
209	ctx->nr_rx_slots = req.nr_rx_slots;
210	ctx->nr_tx_rings = req.nr_tx_rings;
211	ctx->nr_rx_rings = req.nr_rx_rings;
212	ctx->nr_mem_id   = req.nr_mem_id;
213
214	return 0;
215}
216
217/* Single NETMAP_REQ_REGISTER, no use. */
218static int
219port_register(struct TestContext *ctx)
220{
221	struct nmreq_register req;
222	struct nmreq_header hdr;
223	int success;
224	int ret;
225
226	printf("Testing NETMAP_REQ_REGISTER(mode=%d,ringid=%d,"
227	       "flags=0x%llx) on '%s'\n",
228	       ctx->nr_mode, ctx->nr_ringid, (unsigned long long)ctx->nr_flags,
229	       ctx->ifname_ext);
230
231	nmreq_hdr_init(&hdr, ctx->ifname_ext);
232	hdr.nr_reqtype = NETMAP_REQ_REGISTER;
233	hdr.nr_body    = (uintptr_t)&req;
234	hdr.nr_options = (uintptr_t)ctx->nr_opt;
235	memset(&req, 0, sizeof(req));
236	req.nr_mem_id     = ctx->nr_mem_id;
237	req.nr_mode       = ctx->nr_mode;
238	req.nr_ringid     = ctx->nr_ringid;
239	req.nr_flags      = ctx->nr_flags;
240	req.nr_tx_slots   = ctx->nr_tx_slots;
241	req.nr_rx_slots   = ctx->nr_rx_slots;
242	req.nr_tx_rings   = ctx->nr_tx_rings;
243	req.nr_host_tx_rings = ctx->nr_host_tx_rings;
244	req.nr_host_rx_rings = ctx->nr_host_rx_rings;
245	req.nr_rx_rings   = ctx->nr_rx_rings;
246	req.nr_extra_bufs = ctx->nr_extra_bufs;
247	ret               = ioctl(ctx->fd, NIOCCTRL, &hdr);
248	if (ret != 0) {
249		perror("ioctl(/dev/netmap, NIOCCTRL, REGISTER)");
250		return ret;
251	}
252	printf("nr_offset 0x%llx\n", (unsigned long long)req.nr_offset);
253	printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
254	printf("nr_tx_slots %u\n", req.nr_tx_slots);
255	printf("nr_rx_slots %u\n", req.nr_rx_slots);
256	printf("nr_tx_rings %u\n", req.nr_tx_rings);
257	printf("nr_rx_rings %u\n", req.nr_rx_rings);
258	printf("nr_host_tx_rings %u\n", req.nr_host_tx_rings);
259	printf("nr_host_rx_rings %u\n", req.nr_host_rx_rings);
260	printf("nr_mem_id %u\n", req.nr_mem_id);
261	printf("nr_extra_bufs %u\n", req.nr_extra_bufs);
262
263	success = req.nr_memsize && (ctx->nr_mode == req.nr_mode) &&
264		       (ctx->nr_ringid == req.nr_ringid) &&
265		       (ctx->nr_flags == req.nr_flags) &&
266		       ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
267			(ctx->nr_tx_slots == req.nr_tx_slots)) &&
268		       ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
269			(ctx->nr_rx_slots == req.nr_rx_slots)) &&
270		       ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
271			(ctx->nr_tx_rings == req.nr_tx_rings)) &&
272		       ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
273			(ctx->nr_rx_rings == req.nr_rx_rings)) &&
274		       ((!ctx->nr_host_tx_rings && req.nr_host_tx_rings) ||
275			(ctx->nr_host_tx_rings == req.nr_host_tx_rings)) &&
276		       ((!ctx->nr_host_rx_rings && req.nr_host_rx_rings) ||
277			(ctx->nr_host_rx_rings == req.nr_host_rx_rings)) &&
278		       ((!ctx->nr_mem_id && req.nr_mem_id) ||
279			(ctx->nr_mem_id == req.nr_mem_id)) &&
280		       (ctx->nr_extra_bufs == req.nr_extra_bufs);
281	if (!success) {
282		return -1;
283	}
284
285	/* Write back results to the context structure.*/
286	ctx->nr_tx_slots   = req.nr_tx_slots;
287	ctx->nr_rx_slots   = req.nr_rx_slots;
288	ctx->nr_tx_rings   = req.nr_tx_rings;
289	ctx->nr_rx_rings   = req.nr_rx_rings;
290	ctx->nr_host_tx_rings = req.nr_host_tx_rings;
291	ctx->nr_host_rx_rings = req.nr_host_rx_rings;
292	ctx->nr_mem_id     = req.nr_mem_id;
293	ctx->nr_extra_bufs = req.nr_extra_bufs;
294
295	return 0;
296}
297
298static int
299niocregif(struct TestContext *ctx, int netmap_api)
300{
301	struct nmreq req;
302	int success;
303	int ret;
304
305	printf("Testing legacy NIOCREGIF on '%s'\n", ctx->ifname_ext);
306
307	memset(&req, 0, sizeof(req));
308	memcpy(req.nr_name, ctx->ifname_ext, sizeof(req.nr_name));
309	req.nr_name[sizeof(req.nr_name) - 1] = '\0';
310	req.nr_version = netmap_api;
311	req.nr_ringid     = ctx->nr_ringid;
312	req.nr_flags      = ctx->nr_mode | ctx->nr_flags;
313	req.nr_tx_slots   = ctx->nr_tx_slots;
314	req.nr_rx_slots   = ctx->nr_rx_slots;
315	req.nr_tx_rings   = ctx->nr_tx_rings;
316	req.nr_rx_rings   = ctx->nr_rx_rings;
317	req.nr_arg2     = ctx->nr_mem_id;
318	req.nr_arg3 = ctx->nr_extra_bufs;
319
320	ret = ioctl(ctx->fd, NIOCREGIF, &req);
321	if (ret != 0) {
322		perror("ioctl(/dev/netmap, NIOCREGIF)");
323		return ret;
324	}
325
326	printf("nr_offset 0x%x\n", req.nr_offset);
327	printf("nr_memsize  %u\n", req.nr_memsize);
328	printf("nr_tx_slots %u\n", req.nr_tx_slots);
329	printf("nr_rx_slots %u\n", req.nr_rx_slots);
330	printf("nr_tx_rings %u\n", req.nr_tx_rings);
331	printf("nr_rx_rings %u\n", req.nr_rx_rings);
332	printf("nr_version  %d\n", req.nr_version);
333	printf("nr_ringid   %x\n", req.nr_ringid);
334	printf("nr_flags    %x\n", req.nr_flags);
335	printf("nr_arg2     %u\n", req.nr_arg2);
336	printf("nr_arg3     %u\n", req.nr_arg3);
337
338	success = req.nr_memsize &&
339	       (ctx->nr_ringid == req.nr_ringid) &&
340	       ((ctx->nr_mode | ctx->nr_flags) == req.nr_flags) &&
341	       ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
342		(ctx->nr_tx_slots == req.nr_tx_slots)) &&
343	       ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
344		(ctx->nr_rx_slots == req.nr_rx_slots)) &&
345	       ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
346		(ctx->nr_tx_rings == req.nr_tx_rings)) &&
347	       ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
348		(ctx->nr_rx_rings == req.nr_rx_rings)) &&
349	       ((!ctx->nr_mem_id && req.nr_arg2) ||
350		(ctx->nr_mem_id == req.nr_arg2)) &&
351	       (ctx->nr_extra_bufs == req.nr_arg3);
352	if (!success) {
353		return -1;
354	}
355
356	/* Write back results to the context structure.*/
357	ctx->nr_tx_slots   = req.nr_tx_slots;
358	ctx->nr_rx_slots   = req.nr_rx_slots;
359	ctx->nr_tx_rings   = req.nr_tx_rings;
360	ctx->nr_rx_rings   = req.nr_rx_rings;
361	ctx->nr_mem_id     = req.nr_arg2;
362	ctx->nr_extra_bufs = req.nr_arg3;
363
364	return ret;
365}
366
367/* The 11 ABI is the one right before the introduction of the new NIOCCTRL
368 * ABI. The 11 ABI is useful to perform tests with legacy applications
369 * (which use the 11 ABI) and new kernel (which uses 12, or higher).
370 * However, version 14 introduced a change in the layout of struct netmap_if,
371 * so that binary backward compatibility to 11 is not supported anymore.
372 */
373#define NETMAP_API_NIOCREGIF	14
374
375static int
376legacy_regif_default(struct TestContext *ctx)
377{
378	return niocregif(ctx, NETMAP_API_NIOCREGIF);
379}
380
381static int
382legacy_regif_all_nic(struct TestContext *ctx)
383{
384	ctx->nr_mode = NR_REG_ALL_NIC;
385	return niocregif(ctx, NETMAP_API);
386}
387
388static int
389legacy_regif_12(struct TestContext *ctx)
390{
391	ctx->nr_mode = NR_REG_ALL_NIC;
392	return niocregif(ctx, NETMAP_API_NIOCREGIF+1);
393}
394
395static int
396legacy_regif_sw(struct TestContext *ctx)
397{
398	ctx->nr_mode = NR_REG_SW;
399	return niocregif(ctx,  NETMAP_API_NIOCREGIF);
400}
401
402static int
403legacy_regif_future(struct TestContext *ctx)
404{
405	ctx->nr_mode = NR_REG_NIC_SW;
406	/* Test forward compatibility for the legacy ABI. This means
407	 * using an older kernel (with ABI 12 or higher) and a newer
408	 * application (with ABI greater than NETMAP_API). */
409	return niocregif(ctx, NETMAP_API+2);
410}
411
412static int
413legacy_regif_extra_bufs(struct TestContext *ctx)
414{
415	ctx->nr_mode = NR_REG_ALL_NIC;
416	ctx->nr_extra_bufs = 20;	/* arbitrary number of extra bufs */
417	return niocregif(ctx, NETMAP_API_NIOCREGIF);
418}
419
420static int
421legacy_regif_extra_bufs_pipe(struct TestContext *ctx)
422{
423	strncat(ctx->ifname_ext, "{pipeexbuf", sizeof(ctx->ifname_ext));
424	ctx->nr_mode = NR_REG_ALL_NIC;
425	ctx->nr_extra_bufs = 58;	/* arbitrary number of extra bufs */
426
427	return niocregif(ctx, NETMAP_API_NIOCREGIF);
428}
429
430static int
431legacy_regif_extra_bufs_pipe_vale(struct TestContext *ctx)
432{
433	strncpy(ctx->ifname_ext, "valeX1:Y4", sizeof(ctx->ifname_ext));
434	return legacy_regif_extra_bufs_pipe(ctx);
435}
436
437/* Only valid after a successful port_register(). */
438static int
439num_registered_rings(struct TestContext *ctx)
440{
441	if (ctx->nr_flags & NR_TX_RINGS_ONLY) {
442		return ctx->nr_tx_rings;
443	}
444	if (ctx->nr_flags & NR_RX_RINGS_ONLY) {
445		return ctx->nr_rx_rings;
446	}
447
448	return ctx->nr_tx_rings + ctx->nr_rx_rings;
449}
450
451static int
452port_register_hwall_host(struct TestContext *ctx)
453{
454	ctx->nr_mode = NR_REG_NIC_SW;
455	return port_register(ctx);
456}
457
458static int
459port_register_hostall(struct TestContext *ctx)
460{
461	ctx->nr_mode = NR_REG_SW;
462	return port_register(ctx);
463}
464
465static int
466port_register_hwall(struct TestContext *ctx)
467{
468	ctx->nr_mode = NR_REG_ALL_NIC;
469	return port_register(ctx);
470}
471
472static int
473port_register_single_hw_pair(struct TestContext *ctx)
474{
475	ctx->nr_mode   = NR_REG_ONE_NIC;
476	ctx->nr_ringid = 0;
477	return port_register(ctx);
478}
479
480static int
481port_register_single_host_pair(struct TestContext *ctx)
482{
483	ctx->nr_mode   = NR_REG_ONE_SW;
484	ctx->nr_host_tx_rings = 2;
485	ctx->nr_host_rx_rings = 2;
486	ctx->nr_ringid = 1;
487	return port_register(ctx);
488}
489
490static int
491port_register_hostall_many(struct TestContext *ctx)
492{
493	ctx->nr_mode   = NR_REG_SW;
494	ctx->nr_host_tx_rings = 5;
495	ctx->nr_host_rx_rings = 4;
496	return port_register(ctx);
497}
498
499static int
500port_register_hwall_tx(struct TestContext *ctx)
501{
502	ctx->nr_mode = NR_REG_ALL_NIC;
503	ctx->nr_flags |= NR_TX_RINGS_ONLY;
504	return port_register(ctx);
505}
506
507static int
508port_register_hwall_rx(struct TestContext *ctx)
509{
510	ctx->nr_mode = NR_REG_ALL_NIC;
511	ctx->nr_flags |= NR_RX_RINGS_ONLY;
512	return port_register(ctx);
513}
514
515/* NETMAP_REQ_VALE_ATTACH */
516static int
517vale_attach(struct TestContext *ctx)
518{
519	struct nmreq_vale_attach req;
520	struct nmreq_header hdr;
521	char vpname[sizeof(ctx->bdgname) + 1 + sizeof(ctx->ifname_ext)];
522	int ret;
523
524	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext);
525
526	printf("Testing NETMAP_REQ_VALE_ATTACH on '%s'\n", vpname);
527	nmreq_hdr_init(&hdr, vpname);
528	hdr.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
529	hdr.nr_body    = (uintptr_t)&req;
530	memset(&req, 0, sizeof(req));
531	req.reg.nr_mem_id = ctx->nr_mem_id;
532	if (ctx->nr_mode == 0) {
533		ctx->nr_mode = NR_REG_ALL_NIC; /* default */
534	}
535	req.reg.nr_mode = ctx->nr_mode;
536	ret             = ioctl(ctx->fd, NIOCCTRL, &hdr);
537	if (ret != 0) {
538		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_ATTACH)");
539		return ret;
540	}
541	printf("nr_mem_id %u\n", req.reg.nr_mem_id);
542
543	return ((!ctx->nr_mem_id && req.reg.nr_mem_id > 1) ||
544	        (ctx->nr_mem_id == req.reg.nr_mem_id)) &&
545	                       (ctx->nr_flags == req.reg.nr_flags)
546	               ? 0
547	               : -1;
548}
549
550/* NETMAP_REQ_VALE_DETACH */
551static int
552vale_detach(struct TestContext *ctx)
553{
554	struct nmreq_header hdr;
555	struct nmreq_vale_detach req;
556	char vpname[256];
557	int ret;
558
559	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext);
560
561	printf("Testing NETMAP_REQ_VALE_DETACH on '%s'\n", vpname);
562	nmreq_hdr_init(&hdr, vpname);
563	hdr.nr_reqtype = NETMAP_REQ_VALE_DETACH;
564	hdr.nr_body    = (uintptr_t)&req;
565	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
566	if (ret != 0) {
567		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_DETACH)");
568		return ret;
569	}
570
571	return 0;
572}
573
574/* First NETMAP_REQ_VALE_ATTACH, then NETMAP_REQ_VALE_DETACH. */
575static int
576vale_attach_detach(struct TestContext *ctx)
577{
578	int ret;
579
580	if ((ret = vale_attach(ctx)) != 0) {
581		return ret;
582	}
583
584	return vale_detach(ctx);
585}
586
587static int
588vale_attach_detach_host_rings(struct TestContext *ctx)
589{
590	ctx->nr_mode = NR_REG_NIC_SW;
591	return vale_attach_detach(ctx);
592}
593
594/* First NETMAP_REQ_PORT_HDR_SET and the NETMAP_REQ_PORT_HDR_GET
595 * to check that we get the same value. */
596static int
597port_hdr_set_and_get(struct TestContext *ctx)
598{
599	struct nmreq_port_hdr req;
600	struct nmreq_header hdr;
601	int ret;
602
603	printf("Testing NETMAP_REQ_PORT_HDR_SET on '%s'\n", ctx->ifname_ext);
604
605	nmreq_hdr_init(&hdr, ctx->ifname_ext);
606	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
607	hdr.nr_body    = (uintptr_t)&req;
608	memset(&req, 0, sizeof(req));
609	req.nr_hdr_len = ctx->nr_hdr_len;
610	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
611	if (ret != 0) {
612		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
613		return ret;
614	}
615
616	if (req.nr_hdr_len != ctx->nr_hdr_len) {
617		return -1;
618	}
619
620	printf("Testing NETMAP_REQ_PORT_HDR_GET on '%s'\n", ctx->ifname_ext);
621	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
622	req.nr_hdr_len = 0;
623	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
624	if (ret != 0) {
625		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
626		return ret;
627	}
628	printf("nr_hdr_len %u\n", req.nr_hdr_len);
629
630	return (req.nr_hdr_len == ctx->nr_hdr_len) ? 0 : -1;
631}
632
633/*
634 * Possible lengths for the VirtIO network header, as specified by
635 * the standard:
636 *    http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html
637 */
638#define VIRTIO_NET_HDR_LEN				10
639#define VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS	12
640
641static int
642vale_ephemeral_port_hdr_manipulation(struct TestContext *ctx)
643{
644	int ret;
645
646	strncpy(ctx->ifname_ext, "vale:eph0", sizeof(ctx->ifname_ext));
647	ctx->nr_mode = NR_REG_ALL_NIC;
648	if ((ret = port_register(ctx))) {
649		return ret;
650	}
651	/* Try to set and get all the acceptable values. */
652	ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS;
653	if ((ret = port_hdr_set_and_get(ctx))) {
654		return ret;
655	}
656	ctx->nr_hdr_len = 0;
657	if ((ret = port_hdr_set_and_get(ctx))) {
658		return ret;
659	}
660	ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN;
661	if ((ret = port_hdr_set_and_get(ctx))) {
662		return ret;
663	}
664	return 0;
665}
666
667static int
668vale_persistent_port(struct TestContext *ctx)
669{
670	struct nmreq_vale_newif req;
671	struct nmreq_header hdr;
672	int result;
673	int ret;
674
675	strncpy(ctx->ifname_ext, "per4", sizeof(ctx->ifname_ext));
676
677	printf("Testing NETMAP_REQ_VALE_NEWIF on '%s'\n", ctx->ifname_ext);
678
679	nmreq_hdr_init(&hdr, ctx->ifname_ext);
680	hdr.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
681	hdr.nr_body    = (uintptr_t)&req;
682	memset(&req, 0, sizeof(req));
683	req.nr_mem_id   = ctx->nr_mem_id;
684	req.nr_tx_slots = ctx->nr_tx_slots;
685	req.nr_rx_slots = ctx->nr_rx_slots;
686	req.nr_tx_rings = ctx->nr_tx_rings;
687	req.nr_rx_rings = ctx->nr_rx_rings;
688	ret             = ioctl(ctx->fd, NIOCCTRL, &hdr);
689	if (ret != 0) {
690		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
691		return ret;
692	}
693
694	/* Attach the persistent VALE port to a switch and then detach. */
695	result = vale_attach_detach(ctx);
696
697	printf("Testing NETMAP_REQ_VALE_DELIF on '%s'\n", ctx->ifname_ext);
698	hdr.nr_reqtype = NETMAP_REQ_VALE_DELIF;
699	hdr.nr_body    = (uintptr_t)NULL;
700	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
701	if (ret != 0) {
702		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
703		if (result == 0) {
704			result = ret;
705		}
706	}
707
708	return result;
709}
710
711/* Single NETMAP_REQ_POOLS_INFO_GET. */
712static int
713pools_info_get(struct TestContext *ctx)
714{
715	struct nmreq_pools_info req;
716	struct nmreq_header hdr;
717	int ret;
718
719	printf("Testing NETMAP_REQ_POOLS_INFO_GET on '%s'\n", ctx->ifname_ext);
720
721	nmreq_hdr_init(&hdr, ctx->ifname_ext);
722	hdr.nr_reqtype = NETMAP_REQ_POOLS_INFO_GET;
723	hdr.nr_body    = (uintptr_t)&req;
724	memset(&req, 0, sizeof(req));
725	req.nr_mem_id = ctx->nr_mem_id;
726	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
727	if (ret != 0) {
728		perror("ioctl(/dev/netmap, NIOCCTRL, POOLS_INFO_GET)");
729		return ret;
730	}
731	printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
732	printf("nr_mem_id %u\n", req.nr_mem_id);
733	printf("nr_if_pool_offset 0x%llx\n",
734		(unsigned long long)req.nr_if_pool_offset);
735	printf("nr_if_pool_objtotal %u\n", req.nr_if_pool_objtotal);
736	printf("nr_if_pool_objsize %u\n", req.nr_if_pool_objsize);
737	printf("nr_ring_pool_offset 0x%llx\n",
738		(unsigned long long)req.nr_if_pool_offset);
739	printf("nr_ring_pool_objtotal %u\n", req.nr_ring_pool_objtotal);
740	printf("nr_ring_pool_objsize %u\n", req.nr_ring_pool_objsize);
741	printf("nr_buf_pool_offset 0x%llx\n",
742		(unsigned long long)req.nr_buf_pool_offset);
743	printf("nr_buf_pool_objtotal %u\n", req.nr_buf_pool_objtotal);
744	printf("nr_buf_pool_objsize %u\n", req.nr_buf_pool_objsize);
745
746	return req.nr_memsize && req.nr_if_pool_objtotal &&
747	                       req.nr_if_pool_objsize &&
748	                       req.nr_ring_pool_objtotal &&
749	                       req.nr_ring_pool_objsize &&
750	                       req.nr_buf_pool_objtotal &&
751	                       req.nr_buf_pool_objsize
752	               ? 0
753	               : -1;
754}
755
756static int
757pools_info_get_and_register(struct TestContext *ctx)
758{
759	int ret;
760
761	/* Check that we can get pools info before we register
762	 * a netmap interface. */
763	ret = pools_info_get(ctx);
764	if (ret != 0) {
765		return ret;
766	}
767
768	ctx->nr_mode = NR_REG_ONE_NIC;
769	ret          = port_register(ctx);
770	if (ret != 0) {
771		return ret;
772	}
773	ctx->nr_mem_id = 1;
774
775	/* Check that we can get pools info also after we register. */
776	return pools_info_get(ctx);
777}
778
779static int
780pools_info_get_empty_ifname(struct TestContext *ctx)
781{
782	strncpy(ctx->ifname_ext, "", sizeof(ctx->ifname_ext));
783	return pools_info_get(ctx) != 0 ? 0 : -1;
784}
785
786static int
787pipe_master(struct TestContext *ctx)
788{
789	strncat(ctx->ifname_ext, "{pipeid1", sizeof(ctx->ifname_ext));
790	ctx->nr_mode = NR_REG_NIC_SW;
791
792	if (port_register(ctx) == 0) {
793		printf("pipes should not accept NR_REG_NIC_SW\n");
794		return -1;
795	}
796	ctx->nr_mode = NR_REG_ALL_NIC;
797
798	return port_register(ctx);
799}
800
801static int
802pipe_slave(struct TestContext *ctx)
803{
804	strncat(ctx->ifname_ext, "}pipeid2", sizeof(ctx->ifname_ext));
805	ctx->nr_mode = NR_REG_ALL_NIC;
806
807	return port_register(ctx);
808}
809
810/* Test PORT_INFO_GET and POOLS_INFO_GET on a pipe. This is useful to test the
811 * registration request used internall by netmap. */
812static int
813pipe_port_info_get(struct TestContext *ctx)
814{
815	strncat(ctx->ifname_ext, "}pipeid3", sizeof(ctx->ifname_ext));
816
817	return port_info_get(ctx);
818}
819
820static int
821pipe_pools_info_get(struct TestContext *ctx)
822{
823	strncat(ctx->ifname_ext, "{xid", sizeof(ctx->ifname_ext));
824
825	return pools_info_get(ctx);
826}
827
828/* NETMAP_REQ_VALE_POLLING_ENABLE */
829static int
830vale_polling_enable(struct TestContext *ctx)
831{
832	struct nmreq_vale_polling req;
833	struct nmreq_header hdr;
834	char vpname[256];
835	int ret;
836
837	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext);
838	printf("Testing NETMAP_REQ_VALE_POLLING_ENABLE on '%s'\n", vpname);
839
840	nmreq_hdr_init(&hdr, vpname);
841	hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
842	hdr.nr_body    = (uintptr_t)&req;
843	memset(&req, 0, sizeof(req));
844	req.nr_mode             = ctx->nr_mode;
845	req.nr_first_cpu_id     = ctx->nr_first_cpu_id;
846	req.nr_num_polling_cpus = ctx->nr_num_polling_cpus;
847	ret                     = ioctl(ctx->fd, NIOCCTRL, &hdr);
848	if (ret != 0) {
849		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_ENABLE)");
850		return ret;
851	}
852
853	return (req.nr_mode == ctx->nr_mode &&
854	        req.nr_first_cpu_id == ctx->nr_first_cpu_id &&
855	        req.nr_num_polling_cpus == ctx->nr_num_polling_cpus)
856	               ? 0
857	               : -1;
858}
859
860/* NETMAP_REQ_VALE_POLLING_DISABLE */
861static int
862vale_polling_disable(struct TestContext *ctx)
863{
864	struct nmreq_vale_polling req;
865	struct nmreq_header hdr;
866	char vpname[256];
867	int ret;
868
869	snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext);
870	printf("Testing NETMAP_REQ_VALE_POLLING_DISABLE on '%s'\n", vpname);
871
872	nmreq_hdr_init(&hdr, vpname);
873	hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
874	hdr.nr_body    = (uintptr_t)&req;
875	memset(&req, 0, sizeof(req));
876	ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
877	if (ret != 0) {
878		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_DISABLE)");
879		return ret;
880	}
881
882	return 0;
883}
884
885static int
886vale_polling_enable_disable(struct TestContext *ctx)
887{
888	int ret = 0;
889
890	if ((ret = vale_attach(ctx)) != 0) {
891		return ret;
892	}
893
894	ctx->nr_mode             = NETMAP_POLLING_MODE_SINGLE_CPU;
895	ctx->nr_num_polling_cpus = 1;
896	ctx->nr_first_cpu_id     = 0;
897	if ((ret = vale_polling_enable(ctx))) {
898		vale_detach(ctx);
899#ifdef __FreeBSD__
900		/* NETMAP_REQ_VALE_POLLING_DISABLE is disabled on FreeBSD,
901		 * because it is currently broken. We are happy to see that
902		 * it fails. */
903		return 0;
904#else
905		return ret;
906#endif
907	}
908
909	if ((ret = vale_polling_disable(ctx))) {
910		vale_detach(ctx);
911		return ret;
912	}
913
914	return vale_detach(ctx);
915}
916
917static void
918push_option(struct nmreq_option *opt, struct TestContext *ctx)
919{
920	opt->nro_next = (uintptr_t)ctx->nr_opt;
921	ctx->nr_opt   = opt;
922}
923
924static void
925clear_options(struct TestContext *ctx)
926{
927	ctx->nr_opt = NULL;
928}
929
930static int
931checkoption(struct nmreq_option *opt, struct nmreq_option *exp)
932{
933	if (opt->nro_next != exp->nro_next) {
934		printf("nro_next %p expected %p\n",
935		       (void *)(uintptr_t)opt->nro_next,
936		       (void *)(uintptr_t)exp->nro_next);
937		return -1;
938	}
939	if (opt->nro_reqtype != exp->nro_reqtype) {
940		printf("nro_reqtype %u expected %u\n", opt->nro_reqtype,
941		       exp->nro_reqtype);
942		return -1;
943	}
944	if (opt->nro_status != exp->nro_status) {
945		printf("nro_status %u expected %u\n", opt->nro_status,
946		       exp->nro_status);
947		return -1;
948	}
949	return 0;
950}
951
952static int
953unsupported_option(struct TestContext *ctx)
954{
955	struct nmreq_option opt, save;
956
957	printf("Testing unsupported option on %s\n", ctx->ifname_ext);
958
959	memset(&opt, 0, sizeof(opt));
960	opt.nro_reqtype = 1234;
961	push_option(&opt, ctx);
962	save = opt;
963
964	if (port_register_hwall(ctx) >= 0)
965		return -1;
966
967	clear_options(ctx);
968	save.nro_status = EOPNOTSUPP;
969	return checkoption(&opt, &save);
970}
971
972static int
973infinite_options(struct TestContext *ctx)
974{
975	struct nmreq_option opt;
976
977	printf("Testing infinite list of options on %s\n", ctx->ifname_ext);
978
979	opt.nro_reqtype = 1234;
980	push_option(&opt, ctx);
981	opt.nro_next = (uintptr_t)&opt;
982	if (port_register_hwall(ctx) >= 0)
983		return -1;
984	clear_options(ctx);
985	return (errno == EMSGSIZE ? 0 : -1);
986}
987
988#ifdef CONFIG_NETMAP_EXTMEM
989int
990change_param(const char *pname, unsigned long newv, unsigned long *poldv)
991{
992#ifdef __linux__
993	char param[256] = "/sys/module/netmap/parameters/";
994	unsigned long oldv;
995	FILE *f;
996
997	strncat(param, pname, sizeof(param) - 1);
998
999	f = fopen(param, "r+");
1000	if (f == NULL) {
1001		perror(param);
1002		return -1;
1003	}
1004	if (fscanf(f, "%ld", &oldv) != 1) {
1005		perror(param);
1006		fclose(f);
1007		return -1;
1008	}
1009	if (poldv)
1010		*poldv = oldv;
1011	rewind(f);
1012	if (fprintf(f, "%ld\n", newv) < 0) {
1013		perror(param);
1014		fclose(f);
1015		return -1;
1016	}
1017	fclose(f);
1018	printf("change_param: %s: %ld -> %ld\n", pname, oldv, newv);
1019#endif /* __linux__ */
1020	return 0;
1021}
1022
1023static int
1024push_extmem_option(struct TestContext *ctx, const struct nmreq_pools_info *pi,
1025		struct nmreq_opt_extmem *e)
1026{
1027	void *addr;
1028
1029	addr = mmap(NULL, pi->nr_memsize, PROT_READ | PROT_WRITE,
1030	            MAP_ANONYMOUS | MAP_SHARED, -1, 0);
1031	if (addr == MAP_FAILED) {
1032		perror("mmap");
1033		return -1;
1034	}
1035
1036	memset(e, 0, sizeof(*e));
1037	e->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
1038	e->nro_info = *pi;
1039	e->nro_usrptr          = (uintptr_t)addr;
1040
1041	push_option(&e->nro_opt, ctx);
1042
1043	return 0;
1044}
1045
1046static int
1047pop_extmem_option(struct TestContext *ctx, struct nmreq_opt_extmem *exp)
1048{
1049	struct nmreq_opt_extmem *e;
1050	int ret;
1051
1052	e           = (struct nmreq_opt_extmem *)(uintptr_t)ctx->nr_opt;
1053	ctx->nr_opt = (struct nmreq_option *)(uintptr_t)ctx->nr_opt->nro_next;
1054
1055	if ((ret = checkoption(&e->nro_opt, &exp->nro_opt))) {
1056		return ret;
1057	}
1058
1059	if (e->nro_usrptr != exp->nro_usrptr) {
1060		printf("usrptr %" PRIu64 " expected %" PRIu64 "\n",
1061		       e->nro_usrptr, exp->nro_usrptr);
1062		return -1;
1063	}
1064	if (e->nro_info.nr_memsize != exp->nro_info.nr_memsize) {
1065		printf("memsize %" PRIu64 " expected %" PRIu64 "\n",
1066		       e->nro_info.nr_memsize, exp->nro_info.nr_memsize);
1067		return -1;
1068	}
1069
1070	if ((ret = munmap((void *)(uintptr_t)e->nro_usrptr,
1071	                  e->nro_info.nr_memsize)))
1072		return ret;
1073
1074	return 0;
1075}
1076
1077static int
1078_extmem_option(struct TestContext *ctx,
1079		const struct nmreq_pools_info *pi)
1080{
1081	struct nmreq_opt_extmem e, save;
1082	int ret;
1083
1084	if ((ret = push_extmem_option(ctx, pi, &e)) < 0)
1085		return ret;
1086
1087	save = e;
1088
1089	strncpy(ctx->ifname_ext, "vale0:0", sizeof(ctx->ifname_ext));
1090	ctx->nr_tx_slots = 16;
1091	ctx->nr_rx_slots = 16;
1092
1093	if ((ret = port_register_hwall(ctx)))
1094		return ret;
1095
1096	ret = pop_extmem_option(ctx, &save);
1097
1098	return ret;
1099}
1100
1101static size_t
1102pools_info_min_memsize(const struct nmreq_pools_info *pi)
1103{
1104	size_t tot = 0;
1105
1106	tot += pi->nr_if_pool_objtotal * pi->nr_if_pool_objsize;
1107	tot += pi->nr_ring_pool_objtotal * pi->nr_ring_pool_objsize;
1108	tot += pi->nr_buf_pool_objtotal * pi->nr_buf_pool_objsize;
1109
1110	return tot;
1111}
1112
1113/*
1114 * Fill the specification of a netmap memory allocator to be
1115 * used with the 'struct nmreq_opt_extmem' option. Arbitrary
1116 * values are used for the parameters, but with enough netmap
1117 * rings, netmap ifs, and buffers to support a VALE port.
1118 */
1119static void
1120pools_info_fill(struct nmreq_pools_info *pi)
1121{
1122	pi->nr_if_pool_objtotal = 2;
1123	pi->nr_if_pool_objsize = 1024;
1124	pi->nr_ring_pool_objtotal = 64;
1125	pi->nr_ring_pool_objsize = 512;
1126	pi->nr_buf_pool_objtotal = 4096;
1127	pi->nr_buf_pool_objsize = 2048;
1128	pi->nr_memsize = pools_info_min_memsize(pi);
1129}
1130
1131static int
1132extmem_option(struct TestContext *ctx)
1133{
1134	struct nmreq_pools_info	pools_info;
1135
1136	pools_info_fill(&pools_info);
1137
1138	printf("Testing extmem option on vale0:0\n");
1139	return _extmem_option(ctx, &pools_info);
1140}
1141
1142static int
1143bad_extmem_option(struct TestContext *ctx)
1144{
1145	struct nmreq_pools_info	pools_info;
1146
1147	printf("Testing bad extmem option on vale0:0\n");
1148
1149	pools_info_fill(&pools_info);
1150	/* Request a large ring size, to make sure that the kernel
1151	 * rejects our request. */
1152	pools_info.nr_ring_pool_objsize = (1 << 20);
1153
1154	return _extmem_option(ctx, &pools_info) < 0 ? 0 : -1;
1155}
1156
1157static int
1158duplicate_extmem_options(struct TestContext *ctx)
1159{
1160	struct nmreq_opt_extmem e1, save1, e2, save2;
1161	struct nmreq_pools_info	pools_info;
1162	int ret;
1163
1164	printf("Testing duplicate extmem option on vale0:0\n");
1165
1166	pools_info_fill(&pools_info);
1167
1168	if ((ret = push_extmem_option(ctx, &pools_info, &e1)) < 0)
1169		return ret;
1170
1171	if ((ret = push_extmem_option(ctx, &pools_info, &e2)) < 0) {
1172		clear_options(ctx);
1173		return ret;
1174	}
1175
1176	save1 = e1;
1177	save2 = e2;
1178
1179	strncpy(ctx->ifname_ext, "vale0:0", sizeof(ctx->ifname_ext));
1180	ctx->nr_tx_slots = 16;
1181	ctx->nr_rx_slots = 16;
1182
1183	ret = port_register_hwall(ctx);
1184	if (ret >= 0) {
1185		printf("duplicate option not detected\n");
1186		return -1;
1187	}
1188
1189	save2.nro_opt.nro_status = EINVAL;
1190	if ((ret = pop_extmem_option(ctx, &save2)))
1191		return ret;
1192
1193	save1.nro_opt.nro_status = EINVAL;
1194	if ((ret = pop_extmem_option(ctx, &save1)))
1195		return ret;
1196
1197	return 0;
1198}
1199#endif /* CONFIG_NETMAP_EXTMEM */
1200
1201static int
1202push_csb_option(struct TestContext *ctx, struct nmreq_opt_csb *opt)
1203{
1204	size_t csb_size;
1205	int num_entries;
1206	int ret;
1207
1208	ctx->nr_flags |= NR_EXCLUSIVE;
1209
1210	/* Get port info in order to use num_registered_rings(). */
1211	ret = port_info_get(ctx);
1212	if (ret != 0) {
1213		return ret;
1214	}
1215	num_entries = num_registered_rings(ctx);
1216
1217	csb_size = (sizeof(struct nm_csb_atok) + sizeof(struct nm_csb_ktoa)) *
1218	           num_entries;
1219	assert(csb_size > 0);
1220	if (ctx->csb) {
1221		free(ctx->csb);
1222	}
1223	ret = posix_memalign(&ctx->csb, sizeof(struct nm_csb_atok), csb_size);
1224	if (ret != 0) {
1225		printf("Failed to allocate CSB memory\n");
1226		exit(EXIT_FAILURE);
1227	}
1228
1229	memset(opt, 0, sizeof(*opt));
1230	opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
1231	opt->csb_atok            = (uintptr_t)ctx->csb;
1232	opt->csb_ktoa            = (uintptr_t)(((uint8_t *)ctx->csb) +
1233                                    sizeof(struct nm_csb_atok) * num_entries);
1234
1235	printf("Pushing option NETMAP_REQ_OPT_CSB\n");
1236	push_option(&opt->nro_opt, ctx);
1237
1238	return 0;
1239}
1240
1241static int
1242csb_mode(struct TestContext *ctx)
1243{
1244	struct nmreq_opt_csb opt;
1245	int ret;
1246
1247	ret = push_csb_option(ctx, &opt);
1248	if (ret != 0) {
1249		return ret;
1250	}
1251
1252	ret = port_register_hwall(ctx);
1253	clear_options(ctx);
1254
1255	return ret;
1256}
1257
1258static int
1259csb_mode_invalid_memory(struct TestContext *ctx)
1260{
1261	struct nmreq_opt_csb opt;
1262	int ret;
1263
1264	memset(&opt, 0, sizeof(opt));
1265	opt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
1266	opt.csb_atok            = (uintptr_t)0x10;
1267	opt.csb_ktoa            = (uintptr_t)0x800;
1268	push_option(&opt.nro_opt, ctx);
1269
1270	ctx->nr_flags = NR_EXCLUSIVE;
1271	ret           = port_register_hwall(ctx);
1272	clear_options(ctx);
1273
1274	return (ret < 0) ? 0 : -1;
1275}
1276
1277static int
1278sync_kloop_stop(struct TestContext *ctx)
1279{
1280	struct nmreq_header hdr;
1281	int ret;
1282
1283	printf("Testing NETMAP_REQ_SYNC_KLOOP_STOP on '%s'\n", ctx->ifname_ext);
1284
1285	nmreq_hdr_init(&hdr, ctx->ifname_ext);
1286	hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_STOP;
1287	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
1288	if (ret != 0) {
1289		perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_STOP)");
1290	}
1291
1292	return ret;
1293}
1294
1295static void *
1296sync_kloop_worker(void *opaque)
1297{
1298	struct TestContext *ctx = opaque;
1299	struct nmreq_sync_kloop_start req;
1300	struct nmreq_header hdr;
1301	int ret;
1302
1303	printf("Testing NETMAP_REQ_SYNC_KLOOP_START on '%s'\n", ctx->ifname_ext);
1304
1305	nmreq_hdr_init(&hdr, ctx->ifname_ext);
1306	hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_START;
1307	hdr.nr_body    = (uintptr_t)&req;
1308	hdr.nr_options = (uintptr_t)ctx->nr_opt;
1309	memset(&req, 0, sizeof(req));
1310	req.sleep_us = 500;
1311	ret          = ioctl(ctx->fd, NIOCCTRL, &hdr);
1312	if (ret != 0) {
1313		perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_START)");
1314	}
1315
1316	if (ctx->sem) {
1317		sem_post(ctx->sem);
1318	}
1319
1320	pthread_exit(ret ? (void *)THRET_FAILURE : (void *)THRET_SUCCESS);
1321}
1322
1323static int
1324sync_kloop_start_stop(struct TestContext *ctx)
1325{
1326	pthread_t th;
1327	void *thret = THRET_FAILURE;
1328	int ret;
1329
1330	ret = pthread_create(&th, NULL, sync_kloop_worker, ctx);
1331	if (ret != 0) {
1332		printf("pthread_create(kloop): %s\n", strerror(ret));
1333		return -1;
1334	}
1335
1336	ret = sync_kloop_stop(ctx);
1337	if (ret != 0) {
1338		return ret;
1339	}
1340
1341	ret = pthread_join(th, &thret);
1342	if (ret != 0) {
1343		printf("pthread_join(kloop): %s\n", strerror(ret));
1344	}
1345
1346	return thret == THRET_SUCCESS ? 0 : -1;
1347}
1348
1349static int
1350sync_kloop(struct TestContext *ctx)
1351{
1352	int ret;
1353
1354	ret = csb_mode(ctx);
1355	if (ret != 0) {
1356		return ret;
1357	}
1358
1359	return sync_kloop_start_stop(ctx);
1360}
1361
1362static int
1363sync_kloop_eventfds(struct TestContext *ctx)
1364{
1365	struct nmreq_opt_sync_kloop_eventfds *evopt = NULL;
1366	struct nmreq_opt_sync_kloop_mode modeopt;
1367	struct nmreq_option evsave;
1368	int num_entries;
1369	size_t opt_size;
1370	int ret, i;
1371
1372	memset(&modeopt, 0, sizeof(modeopt));
1373	modeopt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_MODE;
1374	modeopt.mode = ctx->sync_kloop_mode;
1375	push_option(&modeopt.nro_opt, ctx);
1376
1377	num_entries = num_registered_rings(ctx);
1378	opt_size    = sizeof(*evopt) + num_entries * sizeof(evopt->eventfds[0]);
1379	evopt = calloc(1, opt_size);
1380	evopt->nro_opt.nro_next    = 0;
1381	evopt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS;
1382	evopt->nro_opt.nro_status  = 0;
1383	evopt->nro_opt.nro_size    = opt_size;
1384	for (i = 0; i < num_entries; i++) {
1385		int efd = eventfd(0, 0);
1386
1387		evopt->eventfds[i].ioeventfd = efd;
1388		efd                        = eventfd(0, 0);
1389		evopt->eventfds[i].irqfd = efd;
1390	}
1391
1392	push_option(&evopt->nro_opt, ctx);
1393	evsave = evopt->nro_opt;
1394
1395	ret = sync_kloop_start_stop(ctx);
1396	if (ret != 0) {
1397		free(evopt);
1398		clear_options(ctx);
1399		return ret;
1400	}
1401#ifdef __linux__
1402	evsave.nro_status = 0;
1403#else  /* !__linux__ */
1404	evsave.nro_status = EOPNOTSUPP;
1405#endif /* !__linux__ */
1406
1407	ret = checkoption(&evopt->nro_opt, &evsave);
1408	free(evopt);
1409	clear_options(ctx);
1410
1411	return ret;
1412}
1413
1414static int
1415sync_kloop_eventfds_all_mode(struct TestContext *ctx,
1416			     uint32_t sync_kloop_mode)
1417{
1418	int ret;
1419
1420	ret = csb_mode(ctx);
1421	if (ret != 0) {
1422		return ret;
1423	}
1424
1425	ctx->sync_kloop_mode = sync_kloop_mode;
1426
1427	return sync_kloop_eventfds(ctx);
1428}
1429
1430static int
1431sync_kloop_eventfds_all(struct TestContext *ctx)
1432{
1433	return sync_kloop_eventfds_all_mode(ctx, 0);
1434}
1435
1436static int
1437sync_kloop_eventfds_all_tx(struct TestContext *ctx)
1438{
1439	struct nmreq_opt_csb opt;
1440	int ret;
1441
1442	ret = push_csb_option(ctx, &opt);
1443	if (ret != 0) {
1444		return ret;
1445	}
1446
1447	ret = port_register_hwall_tx(ctx);
1448	if (ret != 0) {
1449		return ret;
1450	}
1451	clear_options(ctx);
1452
1453	return sync_kloop_eventfds(ctx);
1454}
1455
1456static int
1457sync_kloop_eventfds_all_direct(struct TestContext *ctx)
1458{
1459	return sync_kloop_eventfds_all_mode(ctx,
1460	    NM_OPT_SYNC_KLOOP_DIRECT_TX | NM_OPT_SYNC_KLOOP_DIRECT_RX);
1461}
1462
1463static int
1464sync_kloop_eventfds_all_direct_tx(struct TestContext *ctx)
1465{
1466	return sync_kloop_eventfds_all_mode(ctx,
1467	    NM_OPT_SYNC_KLOOP_DIRECT_TX);
1468}
1469
1470static int
1471sync_kloop_eventfds_all_direct_rx(struct TestContext *ctx)
1472{
1473	return sync_kloop_eventfds_all_mode(ctx,
1474	    NM_OPT_SYNC_KLOOP_DIRECT_RX);
1475}
1476
1477static int
1478sync_kloop_nocsb(struct TestContext *ctx)
1479{
1480	int ret;
1481
1482	ret = port_register_hwall(ctx);
1483	if (ret != 0) {
1484		return ret;
1485	}
1486
1487	/* Sync kloop must fail because we did not use
1488	 * NETMAP_REQ_CSB_ENABLE. */
1489	return sync_kloop_start_stop(ctx) != 0 ? 0 : -1;
1490}
1491
1492static int
1493csb_enable(struct TestContext *ctx)
1494{
1495	struct nmreq_option saveopt;
1496	struct nmreq_opt_csb opt;
1497	struct nmreq_header hdr;
1498	int ret;
1499
1500	ret = push_csb_option(ctx, &opt);
1501	if (ret != 0) {
1502		return ret;
1503	}
1504	saveopt = opt.nro_opt;
1505	saveopt.nro_status = 0;
1506
1507	nmreq_hdr_init(&hdr, ctx->ifname_ext);
1508	hdr.nr_reqtype = NETMAP_REQ_CSB_ENABLE;
1509	hdr.nr_options = (uintptr_t)ctx->nr_opt;
1510	hdr.nr_body = (uintptr_t)NULL;
1511
1512	printf("Testing NETMAP_REQ_CSB_ENABLE on '%s'\n", ctx->ifname_ext);
1513
1514	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
1515	if (ret != 0) {
1516		perror("ioctl(/dev/netmap, NIOCCTRL, CSB_ENABLE)");
1517		return ret;
1518	}
1519
1520	ret = checkoption(&opt.nro_opt, &saveopt);
1521	clear_options(ctx);
1522
1523	return ret;
1524}
1525
1526static int
1527sync_kloop_csb_enable(struct TestContext *ctx)
1528{
1529	int ret;
1530
1531	ctx->nr_flags |= NR_EXCLUSIVE;
1532	ret = port_register_hwall(ctx);
1533	if (ret != 0) {
1534		return ret;
1535	}
1536
1537	ret = csb_enable(ctx);
1538	if (ret != 0) {
1539		return ret;
1540	}
1541
1542	return sync_kloop_start_stop(ctx);
1543}
1544
1545static int
1546sync_kloop_conflict(struct TestContext *ctx)
1547{
1548	struct nmreq_opt_csb opt;
1549	pthread_t th1, th2;
1550	void *thret1 = THRET_FAILURE, *thret2 = THRET_FAILURE;
1551	struct timespec to;
1552	sem_t sem;
1553	int err = 0;
1554	int ret;
1555
1556	ret = push_csb_option(ctx, &opt);
1557	if (ret != 0) {
1558		return ret;
1559	}
1560
1561	ret = port_register_hwall(ctx);
1562	if (ret != 0) {
1563		return ret;
1564	}
1565	clear_options(ctx);
1566
1567	ret = sem_init(&sem, 0, 0);
1568	if (ret != 0) {
1569		printf("sem_init() failed: %s\n", strerror(ret));
1570		return ret;
1571	}
1572	ctx->sem = &sem;
1573
1574	ret = pthread_create(&th1, NULL, sync_kloop_worker, ctx);
1575	err |= ret;
1576	if (ret != 0) {
1577		printf("pthread_create(kloop1): %s\n", strerror(ret));
1578	}
1579
1580	ret = pthread_create(&th2, NULL, sync_kloop_worker, ctx);
1581	err |= ret;
1582	if (ret != 0) {
1583		printf("pthread_create(kloop2): %s\n", strerror(ret));
1584	}
1585
1586	/* Wait for one of the two threads to fail to start the kloop, to
1587	 * avoid a race condition where th1 starts the loop and stops,
1588	 * and after that th2 starts the loop successfully. */
1589	clock_gettime(CLOCK_REALTIME, &to);
1590	to.tv_sec += 2;
1591	ret = sem_timedwait(&sem, &to);
1592	err |= ret;
1593	if (ret != 0) {
1594		printf("sem_timedwait() failed: %s\n", strerror(errno));
1595	}
1596
1597	err |= sync_kloop_stop(ctx);
1598
1599	ret = pthread_join(th1, &thret1);
1600	err |= ret;
1601	if (ret != 0) {
1602		printf("pthread_join(kloop1): %s\n", strerror(ret));
1603	}
1604
1605	ret = pthread_join(th2, &thret2);
1606	err |= ret;
1607	if (ret != 0) {
1608		printf("pthread_join(kloop2): %s %d\n", strerror(ret), ret);
1609	}
1610
1611	sem_destroy(&sem);
1612	ctx->sem = NULL;
1613	if (err) {
1614		return err;
1615	}
1616
1617	/* Check that one of the two failed, while the other one succeeded. */
1618	return ((thret1 == THRET_SUCCESS && thret2 == THRET_FAILURE) ||
1619			(thret1 == THRET_FAILURE && thret2 == THRET_SUCCESS))
1620	               ? 0
1621	               : -1;
1622}
1623
1624static int
1625sync_kloop_eventfds_mismatch(struct TestContext *ctx)
1626{
1627	struct nmreq_opt_csb opt;
1628	int ret;
1629
1630	ret = push_csb_option(ctx, &opt);
1631	if (ret != 0) {
1632		return ret;
1633	}
1634
1635	ret = port_register_hwall_rx(ctx);
1636	if (ret != 0) {
1637		return ret;
1638	}
1639	clear_options(ctx);
1640
1641	/* Deceive num_registered_rings() to trigger a failure of
1642	 * sync_kloop_eventfds(). The latter will think that all the
1643	 * rings were registered, and allocate the wrong number of
1644	 * eventfds. */
1645	ctx->nr_flags &= ~NR_RX_RINGS_ONLY;
1646
1647	return (sync_kloop_eventfds(ctx) != 0) ? 0 : -1;
1648}
1649
1650static int
1651null_port(struct TestContext *ctx)
1652{
1653	int ret;
1654
1655	ctx->nr_mem_id = 1;
1656	ctx->nr_mode = NR_REG_NULL;
1657	ctx->nr_tx_rings = 10;
1658	ctx->nr_rx_rings = 5;
1659	ctx->nr_tx_slots = 256;
1660	ctx->nr_rx_slots = 100;
1661	ret = port_register(ctx);
1662	if (ret != 0) {
1663		return ret;
1664	}
1665	return 0;
1666}
1667
1668static int
1669null_port_all_zero(struct TestContext *ctx)
1670{
1671	int ret;
1672
1673	ctx->nr_mem_id = 1;
1674	ctx->nr_mode = NR_REG_NULL;
1675	ctx->nr_tx_rings = 0;
1676	ctx->nr_rx_rings = 0;
1677	ctx->nr_tx_slots = 0;
1678	ctx->nr_rx_slots = 0;
1679	ret = port_register(ctx);
1680	if (ret != 0) {
1681		return ret;
1682	}
1683	return 0;
1684}
1685
1686static int
1687null_port_sync(struct TestContext *ctx)
1688{
1689	int ret;
1690
1691	ctx->nr_mem_id = 1;
1692	ctx->nr_mode = NR_REG_NULL;
1693	ctx->nr_tx_rings = 10;
1694	ctx->nr_rx_rings = 5;
1695	ctx->nr_tx_slots = 256;
1696	ctx->nr_rx_slots = 100;
1697	ret = port_register(ctx);
1698	if (ret != 0) {
1699		return ret;
1700	}
1701	ret = ioctl(ctx->fd, NIOCTXSYNC, 0);
1702	if (ret != 0) {
1703		return ret;
1704	}
1705	return 0;
1706}
1707
1708static void
1709usage(const char *prog)
1710{
1711	printf("%s -i IFNAME\n"
1712	       "[-j TEST_NUM1[-[TEST_NUM2]] | -[TEST_NUM_2]]\n"
1713	       "[-l (list test cases)]\n",
1714	       prog);
1715}
1716
1717struct mytest {
1718	testfunc_t test;
1719	const char *name;
1720};
1721
1722#define decltest(f)                                                            \
1723	{                                                                      \
1724		.test = f, .name = #f                                          \
1725	}
1726
1727static struct mytest tests[] = {
1728	decltest(port_info_get),
1729	decltest(port_register_hwall_host),
1730	decltest(port_register_hwall),
1731	decltest(port_register_hostall),
1732	decltest(port_register_single_hw_pair),
1733	decltest(port_register_single_host_pair),
1734	decltest(port_register_hostall_many),
1735	decltest(vale_attach_detach),
1736	decltest(vale_attach_detach_host_rings),
1737	decltest(vale_ephemeral_port_hdr_manipulation),
1738	decltest(vale_persistent_port),
1739	decltest(pools_info_get_and_register),
1740	decltest(pools_info_get_empty_ifname),
1741	decltest(pipe_master),
1742	decltest(pipe_slave),
1743	decltest(pipe_port_info_get),
1744	decltest(pipe_pools_info_get),
1745	decltest(vale_polling_enable_disable),
1746	decltest(unsupported_option),
1747	decltest(infinite_options),
1748#ifdef CONFIG_NETMAP_EXTMEM
1749	decltest(extmem_option),
1750	decltest(bad_extmem_option),
1751	decltest(duplicate_extmem_options),
1752#endif /* CONFIG_NETMAP_EXTMEM */
1753	decltest(csb_mode),
1754	decltest(csb_mode_invalid_memory),
1755	decltest(sync_kloop),
1756	decltest(sync_kloop_eventfds_all),
1757	decltest(sync_kloop_eventfds_all_tx),
1758	decltest(sync_kloop_eventfds_all_direct),
1759	decltest(sync_kloop_eventfds_all_direct_tx),
1760	decltest(sync_kloop_eventfds_all_direct_rx),
1761	decltest(sync_kloop_nocsb),
1762	decltest(sync_kloop_csb_enable),
1763	decltest(sync_kloop_conflict),
1764	decltest(sync_kloop_eventfds_mismatch),
1765	decltest(null_port),
1766	decltest(null_port_all_zero),
1767	decltest(null_port_sync),
1768	decltest(legacy_regif_default),
1769	decltest(legacy_regif_all_nic),
1770	decltest(legacy_regif_12),
1771	decltest(legacy_regif_sw),
1772	decltest(legacy_regif_future),
1773	decltest(legacy_regif_extra_bufs),
1774	decltest(legacy_regif_extra_bufs_pipe),
1775	decltest(legacy_regif_extra_bufs_pipe_vale),
1776};
1777
1778static void
1779context_cleanup(struct TestContext *ctx)
1780{
1781	if (ctx->csb) {
1782		free(ctx->csb);
1783		ctx->csb = NULL;
1784	}
1785
1786	close(ctx->fd);
1787	ctx->fd = -1;
1788}
1789
1790static int
1791parse_interval(const char *arg, int *j, int *k)
1792{
1793	const char *scan = arg;
1794	char *rest;
1795
1796	*j = 0;
1797	*k = -1;
1798	if (*scan == '-') {
1799		scan++;
1800		goto get_k;
1801	}
1802	if (!isdigit(*scan))
1803		goto err;
1804	*k = strtol(scan, &rest, 10);
1805	*j = *k - 1;
1806	scan = rest;
1807	if (*scan == '-') {
1808		*k = -1;
1809		scan++;
1810	}
1811get_k:
1812	if (*scan == '\0')
1813		return 0;
1814	if (!isdigit(*scan))
1815		goto err;
1816	*k = strtol(scan, &rest, 10);
1817	scan = rest;
1818	if (!(*scan == '\0'))
1819		goto err;
1820
1821	return 0;
1822
1823err:
1824	fprintf(stderr, "syntax error in '%s', must be num[-[num]] or -[num]\n", arg);
1825	return -1;
1826}
1827
1828#define ARGV_APPEND(_av, _ac, _x)\
1829	do {\
1830		assert((int)(_ac) < (int)(sizeof(_av)/sizeof((_av)[0])));\
1831		(_av)[(_ac)++] = _x;\
1832	} while (0)
1833
1834static void
1835tap_cleanup(int signo)
1836{
1837	const char *av[8];
1838	int ac = 0;
1839
1840	(void)signo;
1841#ifdef __FreeBSD__
1842	ARGV_APPEND(av, ac, "ifconfig");
1843	ARGV_APPEND(av, ac, ctx_.ifname);
1844	ARGV_APPEND(av, ac, "destroy");
1845#else
1846	ARGV_APPEND(av, ac, "ip");
1847	ARGV_APPEND(av, ac, "link");
1848	ARGV_APPEND(av, ac, "del");
1849	ARGV_APPEND(av, ac, ctx_.ifname);
1850#endif
1851	ARGV_APPEND(av, ac, NULL);
1852	if (exec_command(ac, av)) {
1853		printf("Failed to destroy tap interface\n");
1854	}
1855}
1856
1857int
1858main(int argc, char **argv)
1859{
1860	int create_tap = 1;
1861	int num_tests;
1862	int ret  = 0;
1863	int j    = 0;
1864	int k    = -1;
1865	int list = 0;
1866	int opt;
1867	int i;
1868
1869#ifdef __FreeBSD__
1870	PLAIN_REQUIRE_KERNEL_MODULE("if_tap", 0);
1871	PLAIN_REQUIRE_KERNEL_MODULE("netmap", 0);
1872#endif
1873
1874	memset(&ctx_, 0, sizeof(ctx_));
1875
1876	{
1877		struct timespec t;
1878		int idx;
1879
1880		clock_gettime(CLOCK_REALTIME, &t);
1881		srand((unsigned int)t.tv_nsec);
1882		idx = rand() % 8000 + 100;
1883		snprintf(ctx_.ifname, sizeof(ctx_.ifname), "tap%d", idx);
1884		idx = rand() % 800 + 100;
1885		snprintf(ctx_.bdgname, sizeof(ctx_.bdgname), "vale%d", idx);
1886	}
1887
1888	while ((opt = getopt(argc, argv, "hi:j:l")) != -1) {
1889		switch (opt) {
1890		case 'h':
1891			usage(argv[0]);
1892			return 0;
1893
1894		case 'i':
1895			strncpy(ctx_.ifname, optarg, sizeof(ctx_.ifname) - 1);
1896			create_tap = 0;
1897			break;
1898
1899		case 'j':
1900			if (parse_interval(optarg, &j, &k) < 0) {
1901				usage(argv[0]);
1902				return -1;
1903			}
1904			break;
1905
1906		case 'l':
1907			list = 1;
1908			create_tap = 0;
1909			break;
1910
1911		default:
1912			printf("    Unrecognized option %c\n", opt);
1913			usage(argv[0]);
1914			return -1;
1915		}
1916	}
1917
1918	num_tests = sizeof(tests) / sizeof(tests[0]);
1919
1920	if (j < 0 || j >= num_tests || k > num_tests) {
1921		fprintf(stderr, "Test interval %d-%d out of range (%d-%d)\n",
1922				j + 1, k, 1, num_tests + 1);
1923		return -1;
1924	}
1925
1926	if (k < 0)
1927		k = num_tests;
1928
1929	if (list) {
1930		printf("Available tests:\n");
1931		for (i = 0; i < num_tests; i++) {
1932			printf("#%03d: %s\n", i + 1, tests[i].name);
1933		}
1934		return 0;
1935	}
1936
1937	if (create_tap) {
1938		struct sigaction sa;
1939		const char *av[8];
1940		int ac = 0;
1941#ifdef __FreeBSD__
1942		ARGV_APPEND(av, ac, "ifconfig");
1943		ARGV_APPEND(av, ac, ctx_.ifname);
1944		ARGV_APPEND(av, ac, "create");
1945		ARGV_APPEND(av, ac, "up");
1946#else
1947		ARGV_APPEND(av, ac, "ip");
1948		ARGV_APPEND(av, ac, "tuntap");
1949		ARGV_APPEND(av, ac, "add");
1950		ARGV_APPEND(av, ac, "mode");
1951		ARGV_APPEND(av, ac, "tap");
1952		ARGV_APPEND(av, ac, "name");
1953		ARGV_APPEND(av, ac, ctx_.ifname);
1954#endif
1955		ARGV_APPEND(av, ac, NULL);
1956		if (exec_command(ac, av)) {
1957			printf("Failed to create tap interface\n");
1958			return -1;
1959		}
1960
1961		sa.sa_handler = tap_cleanup;
1962		sigemptyset(&sa.sa_mask);
1963		sa.sa_flags = SA_RESTART;
1964		ret         = sigaction(SIGINT, &sa, NULL);
1965		if (ret) {
1966			perror("sigaction(SIGINT)");
1967			goto out;
1968		}
1969		ret = sigaction(SIGTERM, &sa, NULL);
1970		if (ret) {
1971			perror("sigaction(SIGTERM)");
1972			goto out;
1973		}
1974	}
1975
1976	for (i = j; i < k; i++) {
1977		struct TestContext ctxcopy;
1978		int fd;
1979		printf("==> Start of Test #%d [%s]\n", i + 1, tests[i].name);
1980		fd = open("/dev/netmap", O_RDWR);
1981		if (fd < 0) {
1982			perror("open(/dev/netmap)");
1983			ret = fd;
1984			goto out;
1985		}
1986		memcpy(&ctxcopy, &ctx_, sizeof(ctxcopy));
1987		ctxcopy.fd = fd;
1988		memcpy(ctxcopy.ifname_ext, ctxcopy.ifname,
1989			sizeof(ctxcopy.ifname));
1990		ret        = tests[i].test(&ctxcopy);
1991		if (ret != 0) {
1992			printf("Test #%d [%s] failed\n", i + 1, tests[i].name);
1993			goto out;
1994		}
1995		printf("==> Test #%d [%s] successful\n", i + 1, tests[i].name);
1996		context_cleanup(&ctxcopy);
1997	}
1998out:
1999	tap_cleanup(0);
2000
2001	return ret;
2002}
2003