xref: /freebsd/lib/libnetmap/nmport.c (revision fe75646a0234a261c0013bf1840fdac4acaf0cec)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2018 Universita` di Pisa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  *   1. Redistributions of source code must retain the above copyright
12  *      notice, this list of conditions and the following disclaimer.
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/ioctl.h>
33 #include <sys/mman.h>
34 #include <fcntl.h>
35 #include <inttypes.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <net/netmap_user.h>
43 #define LIBNETMAP_NOTHREADSAFE
44 #include "libnetmap.h"
45 
46 struct nmport_cleanup_d {
47 	struct nmport_cleanup_d *next;
48 	void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *);
49 };
50 
51 static void
52 nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c)
53 {
54 	c->next = d->clist;
55 	d->clist = c;
56 }
57 
58 static void
59 nmport_pop_cleanup(struct nmport_d *d)
60 {
61 	struct nmport_cleanup_d *top;
62 
63 	top = d->clist;
64 	d->clist = d->clist->next;
65 	(*top->cleanup)(top, d);
66 	nmctx_free(d->ctx, top);
67 }
68 
69 void nmport_do_cleanup(struct nmport_d *d)
70 {
71 	while (d->clist != NULL) {
72 		nmport_pop_cleanup(d);
73 	}
74 }
75 
76 static struct nmport_d *
77 nmport_new_with_ctx(struct nmctx *ctx)
78 {
79 	struct nmport_d *d;
80 
81 	/* allocate a descriptor */
82 	d = nmctx_malloc(ctx, sizeof(*d));
83 	if (d == NULL) {
84 		nmctx_ferror(ctx, "cannot allocate nmport descriptor");
85 		goto out;
86 	}
87 	memset(d, 0, sizeof(*d));
88 
89 	nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg);
90 
91 	d->ctx = ctx;
92 	d->fd = -1;
93 
94 out:
95 	return d;
96 }
97 
98 struct nmport_d *
99 nmport_new(void)
100 {
101 	struct nmctx *ctx = nmctx_get();
102 	return nmport_new_with_ctx(ctx);
103 }
104 
105 
106 void
107 nmport_delete(struct nmport_d *d)
108 {
109 	nmctx_free(d->ctx, d);
110 }
111 
112 void
113 nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d)
114 {
115 	(void)c;
116 
117 	if (d->extmem == NULL)
118 		return;
119 
120 	nmreq_remove_option(&d->hdr, &d->extmem->nro_opt);
121 	nmctx_free(d->ctx, d->extmem);
122 	d->extmem = NULL;
123 }
124 
125 
126 int
127 nmport_extmem(struct nmport_d *d, void *base, size_t size)
128 {
129 	struct nmctx *ctx = d->ctx;
130 	struct nmport_cleanup_d *clnup = NULL;
131 
132 	if (d->register_done) {
133 		nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name);
134 		errno = EINVAL;
135 		return -1;
136 	}
137 
138 	if (d->extmem != NULL) {
139 		nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name);
140 		errno = EINVAL;
141 		return -1;
142 	}
143 
144 	clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup));
145 	if (clnup == NULL) {
146 		nmctx_ferror(ctx, "failed to allocate cleanup descriptor");
147 		errno = ENOMEM;
148 		return -1;
149 	}
150 
151 	d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem));
152 	if (d->extmem == NULL) {
153 		nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name);
154 		nmctx_free(ctx, clnup);
155 		errno = ENOMEM;
156 		return -1;
157 	}
158 	memset(d->extmem, 0, sizeof(*d->extmem));
159 	d->extmem->nro_usrptr = (uintptr_t)base;
160 	d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
161 	d->extmem->nro_info.nr_memsize = size;
162 	nmreq_push_option(&d->hdr, &d->extmem->nro_opt);
163 
164 	clnup->cleanup = nmport_extmem_cleanup;
165 	nmport_push_cleanup(d, clnup);
166 
167 	return 0;
168 }
169 
170 struct nmport_extmem_from_file_cleanup_d {
171 	struct nmport_cleanup_d up;
172 	void *p;
173 	size_t size;
174 };
175 
176 void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
177 		struct nmport_d *d)
178 {
179 	(void)d;
180 	struct nmport_extmem_from_file_cleanup_d *cc =
181 		(struct nmport_extmem_from_file_cleanup_d *)c;
182 
183 	munmap(cc->p, cc->size);
184 }
185 
186 int
187 nmport_extmem_from_file(struct nmport_d *d, const char *fname)
188 {
189 	struct nmctx *ctx = d->ctx;
190 	int fd = -1;
191 	off_t mapsize;
192 	void *p;
193 	struct nmport_extmem_from_file_cleanup_d *clnup = NULL;
194 
195 	clnup = nmctx_malloc(ctx, sizeof(*clnup));
196 	if (clnup == NULL) {
197 		nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
198 		errno = ENOMEM;
199 		goto fail;
200 	}
201 
202 	fd = open(fname, O_RDWR);
203 	if (fd < 0) {
204 		nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno));
205 		goto fail;
206 	}
207 	mapsize = lseek(fd, 0, SEEK_END);
208 	if (mapsize < 0) {
209 		nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno));
210 		goto fail;
211 	}
212 	p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
213 	if (p == MAP_FAILED) {
214 		nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno));
215 		goto fail;
216 	}
217 	close(fd);
218 
219 	clnup->p = p;
220 	clnup->size = mapsize;
221 	clnup->up.cleanup = nmport_extmem_from_file_cleanup;
222 	nmport_push_cleanup(d, &clnup->up);
223 
224 	if (nmport_extmem(d, p, mapsize) < 0)
225 		goto fail;
226 
227 	return 0;
228 
229 fail:
230 	if (fd >= 0)
231 		close(fd);
232 	if (clnup != NULL) {
233 		if (clnup->p != MAP_FAILED)
234 			nmport_pop_cleanup(d);
235 		else
236 			nmctx_free(ctx, clnup);
237 	}
238 	return -1;
239 }
240 
241 struct nmreq_pools_info*
242 nmport_extmem_getinfo(struct nmport_d *d)
243 {
244 	if (d->extmem == NULL)
245 		return NULL;
246 	return &d->extmem->nro_info;
247 }
248 
249 struct nmport_offset_cleanup_d {
250 	struct nmport_cleanup_d up;
251 	struct nmreq_opt_offsets *opt;
252 };
253 
254 static void
255 nmport_offset_cleanup(struct nmport_cleanup_d *c,
256 		struct nmport_d *d)
257 {
258 	struct nmport_offset_cleanup_d *cc =
259 		(struct nmport_offset_cleanup_d *)c;
260 
261 	nmreq_remove_option(&d->hdr, &cc->opt->nro_opt);
262 	nmctx_free(d->ctx, cc->opt);
263 }
264 
265 int
266 nmport_offset(struct nmport_d *d, uint64_t initial,
267 		uint64_t maxoff, uint64_t bits, uint64_t mingap)
268 {
269 	struct nmctx *ctx = d->ctx;
270 	struct nmreq_opt_offsets *opt;
271 	struct nmport_offset_cleanup_d *clnup = NULL;
272 
273 	clnup = nmctx_malloc(ctx, sizeof(*clnup));
274 	if (clnup == NULL) {
275 		nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
276 		errno = ENOMEM;
277 		return -1;
278 	}
279 
280 	opt = nmctx_malloc(ctx, sizeof(*opt));
281 	if (opt == NULL) {
282 		nmctx_ferror(ctx, "%s: cannot allocate offset option", d->hdr.nr_name);
283 		nmctx_free(ctx, clnup);
284 		errno = ENOMEM;
285 		return -1;
286 	}
287 	memset(opt, 0, sizeof(*opt));
288 	opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_OFFSETS;
289 	opt->nro_offset_bits = bits;
290 	opt->nro_initial_offset = initial;
291 	opt->nro_max_offset = maxoff;
292 	opt->nro_min_gap = mingap;
293 	nmreq_push_option(&d->hdr, &opt->nro_opt);
294 
295 	clnup->up.cleanup = nmport_offset_cleanup;
296 	clnup->opt = opt;
297 	nmport_push_cleanup(d, &clnup->up);
298 
299 	return 0;
300 }
301 
302 /* head of the list of options */
303 static struct nmreq_opt_parser *nmport_opt_parsers;
304 
305 #define NPOPT_PARSER(o)		nmport_opt_##o##_parser
306 #define NPOPT_DESC(o)		nmport_opt_##o##_desc
307 #define NPOPT_NRKEYS(o)		(NPOPT_DESC(o).nr_keys)
308 #define NPOPT_DECL(o, f)						\
309 static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *);			\
310 static struct nmreq_opt_parser NPOPT_DESC(o) = {			\
311 	.prefix = #o,							\
312 	.parse = NPOPT_PARSER(o),					\
313 	.flags = (f),							\
314 	.default_key = -1,						\
315 	.nr_keys = 0,							\
316 	.next = NULL,							\
317 };									\
318 static void __attribute__((constructor))				\
319 nmport_opt_##o##_ctor(void)						\
320 {									\
321 	NPOPT_DESC(o).next = nmport_opt_parsers;			\
322 	nmport_opt_parsers = &NPOPT_DESC(o);				\
323 }
324 struct nmport_key_desc {
325 	struct nmreq_opt_parser *option;
326 	const char *key;
327 	unsigned int flags;
328 	int id;
329 };
330 static void
331 nmport_opt_key_ctor(struct nmport_key_desc *k)
332 {
333 	struct nmreq_opt_parser *o = k->option;
334 	struct nmreq_opt_key *ok;
335 
336 	k->id = o->nr_keys;
337 	ok = &o->keys[k->id];
338 	ok->key = k->key;
339 	ok->id = k->id;
340 	ok->flags = k->flags;
341 	o->nr_keys++;
342 	if (ok->flags & NMREQ_OPTK_DEFAULT)
343 		o->default_key = ok->id;
344 }
345 #define NPKEY_DESC(o, k)	nmport_opt_##o##_key_##k##_desc
346 #define NPKEY_ID(o, k)		(NPKEY_DESC(o, k).id)
347 #define NPKEY_DECL(o, k, f)						\
348 static struct nmport_key_desc NPKEY_DESC(o, k) = {			\
349 	.option = &NPOPT_DESC(o),					\
350 	.key = #k,							\
351 	.flags = (f),							\
352 	.id = -1,							\
353 };									\
354 static void __attribute__((constructor))				\
355 nmport_opt_##o##_key_##k##_ctor(void)					\
356 {									\
357 	nmport_opt_key_ctor(&NPKEY_DESC(o, k));				\
358 }
359 #define nmport_key(p, o, k)	((p)->keys[NPKEY_ID(o, k)])
360 #define nmport_defkey(p, o)	((p)->keys[NPOPT_DESC(o).default_key])
361 
362 NPOPT_DECL(share, 0)
363 	NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
364 NPOPT_DECL(extmem, 0)
365 	NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
366 	NPKEY_DECL(extmem, if_num, 0)
367 	NPKEY_DECL(extmem, if_size, 0)
368 	NPKEY_DECL(extmem, ring_num, 0)
369 	NPKEY_DECL(extmem, ring_size, 0)
370 	NPKEY_DECL(extmem, buf_num, 0)
371 	NPKEY_DECL(extmem, buf_size, 0)
372 NPOPT_DECL(conf, 0)
373 	NPKEY_DECL(conf, rings, 0)
374 	NPKEY_DECL(conf, host_rings, 0)
375 	NPKEY_DECL(conf, slots, 0)
376 	NPKEY_DECL(conf, tx_rings, 0)
377 	NPKEY_DECL(conf, rx_rings, 0)
378 	NPKEY_DECL(conf, host_tx_rings, 0)
379 	NPKEY_DECL(conf, host_rx_rings, 0)
380 	NPKEY_DECL(conf, tx_slots, 0)
381 	NPKEY_DECL(conf, rx_slots, 0)
382 NPOPT_DECL(offset, NMREQ_OPTF_DISABLED)
383 	NPKEY_DECL(offset, initial, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
384 	NPKEY_DECL(offset, bits, 0)
385 
386 
387 static int
388 NPOPT_PARSER(share)(struct nmreq_parse_ctx *p)
389 {
390 	struct nmctx *ctx = p->ctx;
391 	struct nmport_d *d = p->token;
392 	int32_t mem_id;
393 	const char *v = nmport_defkey(p, share);
394 
395 	mem_id = nmreq_get_mem_id(&v, ctx);
396 	if (mem_id < 0)
397 		return -1;
398 	if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) {
399 		nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"",
400 				mem_id, d->reg.nr_mem_id);
401 		errno = EINVAL;
402 		return -1;
403 	}
404 	d->reg.nr_mem_id = mem_id;
405 	return 0;
406 }
407 
408 static int
409 NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p)
410 {
411 	struct nmport_d *d;
412 	struct nmreq_pools_info *pi;
413 	int i;
414 
415 	d = p->token;
416 
417 	if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0)
418 		return -1;
419 
420 	pi = &d->extmem->nro_info;
421 
422 	for  (i = 0; i < NPOPT_NRKEYS(extmem); i++) {
423 		const char *k = p->keys[i];
424 		uint32_t v;
425 
426 		if (k == NULL)
427 			continue;
428 
429 		v = atoi(k);
430 		if (i == NPKEY_ID(extmem, if_num)) {
431 			pi->nr_if_pool_objtotal = v;
432 		} else if (i == NPKEY_ID(extmem, if_size)) {
433 			pi->nr_if_pool_objsize = v;
434 		} else if (i == NPKEY_ID(extmem, ring_num)) {
435 			pi->nr_ring_pool_objtotal = v;
436 		} else if (i == NPKEY_ID(extmem, ring_size)) {
437 			pi->nr_ring_pool_objsize = v;
438 		} else if (i == NPKEY_ID(extmem, buf_num)) {
439 			pi->nr_buf_pool_objtotal = v;
440 		} else if (i == NPKEY_ID(extmem, buf_size)) {
441 			pi->nr_buf_pool_objsize = v;
442 		}
443 	}
444 	return 0;
445 }
446 
447 static int
448 NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
449 {
450 	struct nmport_d *d;
451 
452 	d = p->token;
453 
454 	if (nmport_key(p, conf, rings) != NULL) {
455 		uint16_t nr_rings = atoi(nmport_key(p, conf, rings));
456 		d->reg.nr_tx_rings = nr_rings;
457 		d->reg.nr_rx_rings = nr_rings;
458 	}
459 	if (nmport_key(p, conf, host_rings) != NULL) {
460 		uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings));
461 		d->reg.nr_host_tx_rings = nr_rings;
462 		d->reg.nr_host_rx_rings = nr_rings;
463 	}
464 	if (nmport_key(p, conf, slots) != NULL) {
465 		uint32_t nr_slots = atoi(nmport_key(p, conf, slots));
466 		d->reg.nr_tx_slots = nr_slots;
467 		d->reg.nr_rx_slots = nr_slots;
468 	}
469 	if (nmport_key(p, conf, tx_rings) != NULL) {
470 		d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings));
471 	}
472 	if (nmport_key(p, conf, rx_rings) != NULL) {
473 		d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings));
474 	}
475 	if (nmport_key(p, conf, host_tx_rings) != NULL) {
476 		d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings));
477 	}
478 	if (nmport_key(p, conf, host_rx_rings) != NULL) {
479 		d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings));
480 	}
481 	if (nmport_key(p, conf, tx_slots) != NULL) {
482 		d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots));
483 	}
484 	if (nmport_key(p, conf, rx_slots) != NULL) {
485 		d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots));
486 	}
487 	return 0;
488 }
489 
490 static int
491 NPOPT_PARSER(offset)(struct nmreq_parse_ctx *p)
492 {
493 	struct nmport_d *d;
494 	uint64_t initial, bits;
495 
496 	d = p->token;
497 
498 	initial = atoi(nmport_key(p, offset, initial));
499 	bits = 0;
500 	if (nmport_key(p, offset, bits) != NULL)
501 		bits = atoi(nmport_key(p, offset, bits));
502 
503 	return nmport_offset(d, initial, initial, bits, 0);
504 }
505 
506 
507 void
508 nmport_disable_option(const char *opt)
509 {
510 	struct nmreq_opt_parser *p;
511 
512 	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
513 		if (!strcmp(p->prefix, opt)) {
514 			p->flags |= NMREQ_OPTF_DISABLED;
515 		}
516 	}
517 }
518 
519 int
520 nmport_enable_option(const char *opt)
521 {
522 	struct nmreq_opt_parser *p;
523 
524 	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
525 		if (!strcmp(p->prefix, opt)) {
526 			p->flags &= ~NMREQ_OPTF_DISABLED;
527 			return 0;
528 		}
529 	}
530 	errno = EOPNOTSUPP;
531 	return -1;
532 }
533 
534 
535 int
536 nmport_parse(struct nmport_d *d, const char *ifname)
537 {
538 	const char *scan = ifname;
539 
540 	if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) {
541 		goto err;
542 	}
543 
544 	/* parse the register request */
545 	if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) {
546 		goto err;
547 	}
548 
549 	/* parse the options, if any */
550 	if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) {
551 		goto err;
552 	}
553 	return 0;
554 
555 err:
556 	nmport_undo_parse(d);
557 	return -1;
558 }
559 
560 void
561 nmport_undo_parse(struct nmport_d *d)
562 {
563 	nmport_do_cleanup(d);
564 	memset(&d->reg, 0, sizeof(d->reg));
565 	memset(&d->hdr, 0, sizeof(d->hdr));
566 }
567 
568 struct nmport_d *
569 nmport_prepare(const char *ifname)
570 {
571 	struct nmport_d *d;
572 
573 	/* allocate a descriptor */
574 	d = nmport_new();
575 	if (d == NULL)
576 		goto err;
577 
578 	/* parse the header */
579 	if (nmport_parse(d, ifname) < 0)
580 		goto err;
581 
582 	return d;
583 
584 err:
585 	nmport_undo_prepare(d);
586 	return NULL;
587 }
588 
589 void
590 nmport_undo_prepare(struct nmport_d *d)
591 {
592 	if (d == NULL)
593 		return;
594 	nmport_undo_parse(d);
595 	nmport_delete(d);
596 }
597 
598 int
599 nmport_register(struct nmport_d *d)
600 {
601 	struct nmctx *ctx = d->ctx;
602 
603 	if (d->register_done) {
604 		errno = EINVAL;
605 		nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name);
606 		return -1;
607 	}
608 
609 	d->fd = open("/dev/netmap", O_RDWR);
610 	if (d->fd < 0) {
611 		nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno));
612 		goto err;
613 	}
614 
615 	if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) {
616 		struct nmreq_option *o;
617 		int option_errors = 0;
618 
619 		nmreq_foreach_option(&d->hdr, o) {
620 			if (o->nro_status) {
621 				nmctx_ferror(ctx, "%s: option %s: %s",
622 						d->hdr.nr_name,
623 						nmreq_option_name(o->nro_reqtype),
624 						strerror(o->nro_status));
625 				option_errors++;
626 			}
627 
628 		}
629 		if (!option_errors)
630 			nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno));
631 		goto err;
632 	}
633 
634 	d->register_done = 1;
635 
636 	return 0;
637 
638 err:
639 	nmport_undo_register(d);
640 	return -1;
641 }
642 
643 void
644 nmport_undo_register(struct nmport_d *d)
645 {
646 	if (d->fd >= 0)
647 		close(d->fd);
648 	d->fd = -1;
649 	d->register_done = 0;
650 }
651 
652 /* lookup the mem_id in the mem-list: do a new mmap() if
653  * not found, reuse existing otherwise
654  */
655 int
656 nmport_mmap(struct nmport_d *d)
657 {
658 	struct nmctx *ctx = d->ctx;
659 	struct nmem_d *m = NULL;
660 	u_int num_tx, num_rx;
661 	unsigned int i;
662 
663 	if (d->mmap_done) {
664 		errno = EINVAL;
665 		nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name);
666 		return -1;
667 	}
668 
669 	if (!d->register_done) {
670 		errno = EINVAL;
671 		nmctx_ferror(ctx, "cannot map unregistered port");
672 		return -1;
673 	}
674 
675 	nmctx_lock(ctx);
676 
677 	for (m = ctx->mem_descs; m != NULL; m = m->next)
678 		if (m->mem_id == d->reg.nr_mem_id)
679 			break;
680 
681 	if (m == NULL) {
682 		m = nmctx_malloc(ctx, sizeof(*m));
683 		if (m == NULL) {
684 			nmctx_ferror(ctx, "cannot allocate memory descriptor");
685 			goto err;
686 		}
687 		memset(m, 0, sizeof(*m));
688 		if (d->extmem != NULL) {
689 			m->mem = (void *)((uintptr_t)d->extmem->nro_usrptr);
690 			m->size = d->extmem->nro_info.nr_memsize;
691 			m->is_extmem = 1;
692 		} else {
693 			m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE,
694 					MAP_SHARED, d->fd, 0);
695 			if (m->mem == MAP_FAILED) {
696 				nmctx_ferror(ctx, "mmap: %s", strerror(errno));
697 				goto err;
698 			}
699 			m->size = d->reg.nr_memsize;
700 		}
701 		m->mem_id = d->reg.nr_mem_id;
702 		m->next = ctx->mem_descs;
703 		if (ctx->mem_descs != NULL)
704 			ctx->mem_descs->prev = m;
705 		ctx->mem_descs = m;
706 	}
707 	m->refcount++;
708 
709 	nmctx_unlock(ctx);
710 
711 	d->mem = m;
712 
713 	d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset);
714 
715 	num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
716 	for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
717 		;
718 	d->cur_tx_ring = d->first_tx_ring = i;
719 	for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
720 		;
721 	d->last_tx_ring = i - 1;
722 
723 	num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
724 	for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
725 		;
726 	d->cur_rx_ring = d->first_rx_ring = i;
727 	for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
728 		;
729 	d->last_rx_ring = i - 1;
730 
731 	d->mmap_done = 1;
732 
733 	return 0;
734 
735 err:
736 	nmctx_unlock(ctx);
737 	nmport_undo_mmap(d);
738 	return -1;
739 }
740 
741 void
742 nmport_undo_mmap(struct nmport_d *d)
743 {
744 	struct nmem_d *m;
745 	struct nmctx *ctx = d->ctx;
746 
747 	m = d->mem;
748 	if (m == NULL)
749 		return;
750 	nmctx_lock(ctx);
751 	m->refcount--;
752 	if (m->refcount <= 0) {
753 		if (!m->is_extmem && m->mem != MAP_FAILED)
754 			munmap(m->mem, m->size);
755 		/* extract from the list and free */
756 		if (m->next != NULL)
757 			m->next->prev = m->prev;
758 		if (m->prev != NULL)
759 			m->prev->next = m->next;
760 		else
761 			ctx->mem_descs = m->next;
762 		nmctx_free(ctx, m);
763 		d->mem = NULL;
764 	}
765 	nmctx_unlock(ctx);
766 	d->mmap_done = 0;
767 	d->mem = NULL;
768 	d->nifp = NULL;
769 	d->first_tx_ring = 0;
770 	d->last_tx_ring = 0;
771 	d->first_rx_ring = 0;
772 	d->last_rx_ring = 0;
773 	d->cur_tx_ring = 0;
774 	d->cur_rx_ring = 0;
775 }
776 
777 int
778 nmport_open_desc(struct nmport_d *d)
779 {
780 	if (nmport_register(d) < 0)
781 		goto err;
782 
783 	if (nmport_mmap(d) < 0)
784 		goto err;
785 
786 	return 0;
787 err:
788 	nmport_undo_open_desc(d);
789 	return -1;
790 }
791 
792 void
793 nmport_undo_open_desc(struct nmport_d *d)
794 {
795 	nmport_undo_mmap(d);
796 	nmport_undo_register(d);
797 }
798 
799 
800 struct nmport_d *
801 nmport_open(const char *ifname)
802 {
803 	struct nmport_d *d;
804 
805 	/* prepare the descriptor */
806 	d = nmport_prepare(ifname);
807 	if (d == NULL)
808 		goto err;
809 
810 	/* open netmap and register */
811 	if (nmport_open_desc(d) < 0)
812 		goto err;
813 
814 	return d;
815 
816 err:
817 	nmport_close(d);
818 	return NULL;
819 }
820 
821 void
822 nmport_close(struct nmport_d *d)
823 {
824 	if (d == NULL)
825 		return;
826 	nmport_undo_open_desc(d);
827 	nmport_undo_prepare(d);
828 }
829 
830 struct nmport_d *
831 nmport_clone(struct nmport_d *d)
832 {
833 	struct nmport_d *c;
834 	struct nmctx *ctx;
835 
836 	ctx = d->ctx;
837 
838 	if (d->extmem != NULL && !d->register_done) {
839 		errno = EINVAL;
840 		nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem");
841 		return NULL;
842 	}
843 
844 	c = nmport_new_with_ctx(ctx);
845 	if (c == NULL)
846 		return NULL;
847 	/* copy the output of parse */
848 	c->hdr = d->hdr;
849 	/* redirect the pointer to the body */
850 	c->hdr.nr_body = (uintptr_t)&c->reg;
851 	/* options are not cloned */
852 	c->hdr.nr_options = 0;
853 	c->reg = d->reg; /* this also copies the mem_id */
854 	/* put the new port in an un-registered, unmapped state */
855 	c->fd = -1;
856 	c->nifp = NULL;
857 	c->register_done = 0;
858 	c->mem = NULL;
859 	c->extmem = NULL;
860 	c->mmap_done = 0;
861 	c->first_tx_ring = 0;
862 	c->last_tx_ring = 0;
863 	c->first_rx_ring = 0;
864 	c->last_rx_ring = 0;
865 	c->cur_tx_ring = 0;
866 	c->cur_rx_ring = 0;
867 
868 	return c;
869 }
870 
871 int
872 nmport_inject(struct nmport_d *d, const void *buf, size_t size)
873 {
874 	u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
875 		ri = d->cur_tx_ring;
876 
877 	for (c = 0; c < n ; c++, ri++) {
878 		/* compute current ring to use */
879 		struct netmap_ring *ring;
880 		uint32_t i, j, idx;
881 		size_t rem;
882 
883 		if (ri > d->last_tx_ring)
884 			ri = d->first_tx_ring;
885 		ring = NETMAP_TXRING(d->nifp, ri);
886 		rem = size;
887 		j = ring->cur;
888 		while (rem > ring->nr_buf_size && j != ring->tail) {
889 			rem -= ring->nr_buf_size;
890 			j = nm_ring_next(ring, j);
891 		}
892 		if (j == ring->tail && rem > 0)
893 			continue;
894 		i = ring->cur;
895 		while (i != j) {
896 			idx = ring->slot[i].buf_idx;
897 			ring->slot[i].len = ring->nr_buf_size;
898 			ring->slot[i].flags = NS_MOREFRAG;
899 			nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
900 			i = nm_ring_next(ring, i);
901 			buf = (char *)buf + ring->nr_buf_size;
902 		}
903 		idx = ring->slot[i].buf_idx;
904 		ring->slot[i].len = rem;
905 		ring->slot[i].flags = 0;
906 		nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
907 		ring->head = ring->cur = nm_ring_next(ring, i);
908 		d->cur_tx_ring = ri;
909 		return size;
910 	}
911 	return 0; /* fail */
912 }
913