xref: /freebsd-13-stable/lib/libnetmap/nmport.c (revision 17da660ad5b3b9cd90e164dd4dbb9beaa7203054)
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
nmport_push_cleanup(struct nmport_d * d,struct nmport_cleanup_d * c)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
nmport_pop_cleanup(struct nmport_d * d)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 
nmport_do_cleanup(struct nmport_d * d)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 *
nmport_new_with_ctx(struct nmctx * ctx)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 *
nmport_new(void)99 nmport_new(void)
100 {
101 	struct nmctx *ctx = nmctx_get();
102 	return nmport_new_with_ctx(ctx);
103 }
104 
105 
106 void
nmport_delete(struct nmport_d * d)107 nmport_delete(struct nmport_d *d)
108 {
109 	nmctx_free(d->ctx, d);
110 }
111 
112 void
nmport_extmem_cleanup(struct nmport_cleanup_d * c,struct nmport_d * d)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
nmport_extmem(struct nmport_d * d,void * base,size_t size)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 
nmport_extmem_from_file_cleanup(struct nmport_cleanup_d * c,struct nmport_d * d)176 void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
177 		struct nmport_d *d)
178 {
179 	struct nmport_extmem_from_file_cleanup_d *cc =
180 		(struct nmport_extmem_from_file_cleanup_d *)c;
181 
182 	munmap(cc->p, cc->size);
183 }
184 
185 int
nmport_extmem_from_file(struct nmport_d * d,const char * fname)186 nmport_extmem_from_file(struct nmport_d *d, const char *fname)
187 {
188 	struct nmctx *ctx = d->ctx;
189 	int fd = -1;
190 	off_t mapsize;
191 	void *p;
192 	struct nmport_extmem_from_file_cleanup_d *clnup = NULL;
193 
194 	clnup = nmctx_malloc(ctx, sizeof(*clnup));
195 	if (clnup == NULL) {
196 		nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
197 		errno = ENOMEM;
198 		goto fail;
199 	}
200 
201 	fd = open(fname, O_RDWR);
202 	if (fd < 0) {
203 		nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno));
204 		goto fail;
205 	}
206 	mapsize = lseek(fd, 0, SEEK_END);
207 	if (mapsize < 0) {
208 		nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno));
209 		goto fail;
210 	}
211 	p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
212 	if (p == MAP_FAILED) {
213 		nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno));
214 		goto fail;
215 	}
216 	close(fd);
217 
218 	clnup->p = p;
219 	clnup->size = mapsize;
220 	clnup->up.cleanup = nmport_extmem_from_file_cleanup;
221 	nmport_push_cleanup(d, &clnup->up);
222 
223 	if (nmport_extmem(d, p, mapsize) < 0)
224 		goto fail;
225 
226 	return 0;
227 
228 fail:
229 	if (fd >= 0)
230 		close(fd);
231 	if (clnup != NULL) {
232 		if (clnup->p != MAP_FAILED)
233 			nmport_pop_cleanup(d);
234 		else
235 			nmctx_free(ctx, clnup);
236 	}
237 	return -1;
238 }
239 
240 struct nmreq_pools_info*
nmport_extmem_getinfo(struct nmport_d * d)241 nmport_extmem_getinfo(struct nmport_d *d)
242 {
243 	if (d->extmem == NULL)
244 		return NULL;
245 	return &d->extmem->nro_info;
246 }
247 
248 /* head of the list of options */
249 static struct nmreq_opt_parser *nmport_opt_parsers;
250 
251 #define NPOPT_PARSER(o)		nmport_opt_##o##_parser
252 #define NPOPT_DESC(o)		nmport_opt_##o##_desc
253 #define NPOPT_NRKEYS(o)		(NPOPT_DESC(o).nr_keys)
254 #define NPOPT_DECL(o, f)						\
255 static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *);			\
256 static struct nmreq_opt_parser NPOPT_DESC(o) = {			\
257 	.prefix = #o,							\
258 	.parse = NPOPT_PARSER(o),					\
259 	.flags = (f),							\
260 	.default_key = -1,						\
261 	.nr_keys = 0,							\
262 	.next = NULL,							\
263 };									\
264 static void __attribute__((constructor))				\
265 nmport_opt_##o##_ctor(void)						\
266 {									\
267 	NPOPT_DESC(o).next = nmport_opt_parsers;			\
268 	nmport_opt_parsers = &NPOPT_DESC(o);				\
269 }
270 struct nmport_key_desc {
271 	struct nmreq_opt_parser *option;
272 	const char *key;
273 	unsigned int flags;
274 	int id;
275 };
276 static void
nmport_opt_key_ctor(struct nmport_key_desc * k)277 nmport_opt_key_ctor(struct nmport_key_desc *k)
278 {
279 	struct nmreq_opt_parser *o = k->option;
280 	struct nmreq_opt_key *ok;
281 
282 	k->id = o->nr_keys;
283 	ok = &o->keys[k->id];
284 	ok->key = k->key;
285 	ok->id = k->id;
286 	ok->flags = k->flags;
287 	o->nr_keys++;
288 	if (ok->flags & NMREQ_OPTK_DEFAULT)
289 		o->default_key = ok->id;
290 }
291 #define NPKEY_DESC(o, k)	nmport_opt_##o##_key_##k##_desc
292 #define NPKEY_ID(o, k)		(NPKEY_DESC(o, k).id)
293 #define NPKEY_DECL(o, k, f)						\
294 static struct nmport_key_desc NPKEY_DESC(o, k) = {			\
295 	.option = &NPOPT_DESC(o),					\
296 	.key = #k,							\
297 	.flags = (f),							\
298 	.id = -1,							\
299 };									\
300 static void __attribute__((constructor))				\
301 nmport_opt_##o##_key_##k##_ctor(void)					\
302 {									\
303 	nmport_opt_key_ctor(&NPKEY_DESC(o, k));				\
304 }
305 #define nmport_key(p, o, k)	((p)->keys[NPKEY_ID(o, k)])
306 #define nmport_defkey(p, o)	((p)->keys[NPOPT_DESC(o).default_key])
307 
308 NPOPT_DECL(share, 0)
309 	NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
310 NPOPT_DECL(extmem, 0)
311 	NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
312 	NPKEY_DECL(extmem, if_num, 0)
313 	NPKEY_DECL(extmem, if_size, 0)
314 	NPKEY_DECL(extmem, ring_num, 0)
315 	NPKEY_DECL(extmem, ring_size, 0)
316 	NPKEY_DECL(extmem, buf_num, 0)
317 	NPKEY_DECL(extmem, buf_size, 0)
318 NPOPT_DECL(conf, 0)
319 	NPKEY_DECL(conf, rings, 0)
320 	NPKEY_DECL(conf, host_rings, 0)
321 	NPKEY_DECL(conf, slots, 0)
322 	NPKEY_DECL(conf, tx_rings, 0)
323 	NPKEY_DECL(conf, rx_rings, 0)
324 	NPKEY_DECL(conf, host_tx_rings, 0)
325 	NPKEY_DECL(conf, host_rx_rings, 0)
326 	NPKEY_DECL(conf, tx_slots, 0)
327 	NPKEY_DECL(conf, rx_slots, 0)
328 
329 
330 static int
NPOPT_PARSER(share)331 NPOPT_PARSER(share)(struct nmreq_parse_ctx *p)
332 {
333 	struct nmctx *ctx = p->ctx;
334 	struct nmport_d *d = p->token;
335 	int32_t mem_id;
336 	const char *v = nmport_defkey(p, share);
337 
338 	mem_id = nmreq_get_mem_id(&v, ctx);
339 	if (mem_id < 0)
340 		return -1;
341 	if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) {
342 		nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"",
343 				mem_id, d->reg.nr_mem_id);
344 		errno = EINVAL;
345 		return -1;
346 	}
347 	d->reg.nr_mem_id = mem_id;
348 	return 0;
349 }
350 
351 static int
NPOPT_PARSER(extmem)352 NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p)
353 {
354 	struct nmport_d *d;
355 	struct nmreq_pools_info *pi;
356 	int i;
357 
358 	d = p->token;
359 
360 	if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0)
361 		return -1;
362 
363 	pi = &d->extmem->nro_info;
364 
365 	for  (i = 0; i < NPOPT_NRKEYS(extmem); i++) {
366 		const char *k = p->keys[i];
367 		uint32_t v;
368 
369 		if (k == NULL)
370 			continue;
371 
372 		v = atoi(k);
373 		if (i == NPKEY_ID(extmem, if_num)) {
374 			pi->nr_if_pool_objtotal = v;
375 		} else if (i == NPKEY_ID(extmem, if_size)) {
376 			pi->nr_if_pool_objsize = v;
377 		} else if (i == NPKEY_ID(extmem, ring_num)) {
378 			pi->nr_ring_pool_objtotal = v;
379 		} else if (i == NPKEY_ID(extmem, ring_size)) {
380 			pi->nr_ring_pool_objsize = v;
381 		} else if (i == NPKEY_ID(extmem, buf_num)) {
382 			pi->nr_buf_pool_objtotal = v;
383 		} else if (i == NPKEY_ID(extmem, buf_size)) {
384 			pi->nr_buf_pool_objsize = v;
385 		}
386 	}
387 	return 0;
388 }
389 
390 static int
NPOPT_PARSER(conf)391 NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
392 {
393 	struct nmport_d *d;
394 
395 	d = p->token;
396 
397 	if (nmport_key(p, conf, rings) != NULL) {
398 		uint16_t nr_rings = atoi(nmport_key(p, conf, rings));
399 		d->reg.nr_tx_rings = nr_rings;
400 		d->reg.nr_rx_rings = nr_rings;
401 	}
402 	if (nmport_key(p, conf, host_rings) != NULL) {
403 		uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings));
404 		d->reg.nr_host_tx_rings = nr_rings;
405 		d->reg.nr_host_rx_rings = nr_rings;
406 	}
407 	if (nmport_key(p, conf, slots) != NULL) {
408 		uint32_t nr_slots = atoi(nmport_key(p, conf, slots));
409 		d->reg.nr_tx_slots = nr_slots;
410 		d->reg.nr_rx_slots = nr_slots;
411 	}
412 	if (nmport_key(p, conf, tx_rings) != NULL) {
413 		d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings));
414 	}
415 	if (nmport_key(p, conf, rx_rings) != NULL) {
416 		d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings));
417 	}
418 	if (nmport_key(p, conf, host_tx_rings) != NULL) {
419 		d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings));
420 	}
421 	if (nmport_key(p, conf, host_rx_rings) != NULL) {
422 		d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings));
423 	}
424 	if (nmport_key(p, conf, tx_slots) != NULL) {
425 		d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots));
426 	}
427 	if (nmport_key(p, conf, rx_slots) != NULL) {
428 		d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots));
429 	}
430 	return 0;
431 }
432 
433 void
nmport_disable_option(const char * opt)434 nmport_disable_option(const char *opt)
435 {
436 	struct nmreq_opt_parser *p;
437 
438 	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
439 		if (!strcmp(p->prefix, opt)) {
440 			p->flags |= NMREQ_OPTF_DISABLED;
441 		}
442 	}
443 }
444 
445 int
nmport_enable_option(const char * opt)446 nmport_enable_option(const char *opt)
447 {
448 	struct nmreq_opt_parser *p;
449 
450 	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
451 		if (!strcmp(p->prefix, opt)) {
452 			p->flags &= ~NMREQ_OPTF_DISABLED;
453 			return 0;
454 		}
455 	}
456 	errno = EOPNOTSUPP;
457 	return -1;
458 }
459 
460 
461 int
nmport_parse(struct nmport_d * d,const char * ifname)462 nmport_parse(struct nmport_d *d, const char *ifname)
463 {
464 	const char *scan = ifname;
465 
466 	if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) {
467 		goto err;
468 	}
469 
470 	/* parse the register request */
471 	if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) {
472 		goto err;
473 	}
474 
475 	/* parse the options, if any */
476 	if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) {
477 		goto err;
478 	}
479 	return 0;
480 
481 err:
482 	nmport_undo_parse(d);
483 	return -1;
484 }
485 
486 void
nmport_undo_parse(struct nmport_d * d)487 nmport_undo_parse(struct nmport_d *d)
488 {
489 	nmport_do_cleanup(d);
490 	memset(&d->reg, 0, sizeof(d->reg));
491 	memset(&d->hdr, 0, sizeof(d->hdr));
492 }
493 
494 struct nmport_d *
nmport_prepare(const char * ifname)495 nmport_prepare(const char *ifname)
496 {
497 	struct nmport_d *d;
498 
499 	/* allocate a descriptor */
500 	d = nmport_new();
501 	if (d == NULL)
502 		goto err;
503 
504 	/* parse the header */
505 	if (nmport_parse(d, ifname) < 0)
506 		goto err;
507 
508 	return d;
509 
510 err:
511 	nmport_undo_prepare(d);
512 	return NULL;
513 }
514 
515 void
nmport_undo_prepare(struct nmport_d * d)516 nmport_undo_prepare(struct nmport_d *d)
517 {
518 	if (d == NULL)
519 		return;
520 	nmport_undo_parse(d);
521 	nmport_delete(d);
522 }
523 
524 int
nmport_register(struct nmport_d * d)525 nmport_register(struct nmport_d *d)
526 {
527 	struct nmctx *ctx = d->ctx;
528 
529 	if (d->register_done) {
530 		errno = EINVAL;
531 		nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name);
532 		return -1;
533 	}
534 
535 	d->fd = open("/dev/netmap", O_RDWR);
536 	if (d->fd < 0) {
537 		nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno));
538 		goto err;
539 	}
540 
541 	if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) {
542 		struct nmreq_option *o;
543 		int option_errors = 0;
544 
545 		nmreq_foreach_option(&d->hdr, o) {
546 			if (o->nro_status) {
547 				nmctx_ferror(ctx, "%s: option %s: %s",
548 						d->hdr.nr_name,
549 						nmreq_option_name(o->nro_reqtype),
550 						strerror(o->nro_status));
551 				option_errors++;
552 			}
553 
554 		}
555 		if (!option_errors)
556 			nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno));
557 		goto err;
558 	}
559 
560 	d->register_done = 1;
561 
562 	return 0;
563 
564 err:
565 	nmport_undo_register(d);
566 	return -1;
567 }
568 
569 void
nmport_undo_register(struct nmport_d * d)570 nmport_undo_register(struct nmport_d *d)
571 {
572 	if (d->fd >= 0)
573 		close(d->fd);
574 	d->fd = -1;
575 	d->register_done = 0;
576 }
577 
578 /* lookup the mem_id in the mem-list: do a new mmap() if
579  * not found, reuse existing otherwise
580  */
581 int
nmport_mmap(struct nmport_d * d)582 nmport_mmap(struct nmport_d *d)
583 {
584 	struct nmctx *ctx = d->ctx;
585 	struct nmem_d *m = NULL;
586 	u_int num_tx, num_rx;
587 	int i;
588 
589 	if (d->mmap_done) {
590 		errno = EINVAL;
591 		nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name);
592 		return -1;
593 	}
594 
595 	if (!d->register_done) {
596 		errno = EINVAL;
597 		nmctx_ferror(ctx, "cannot map unregistered port");
598 		return -1;
599 	}
600 
601 	nmctx_lock(ctx);
602 
603 	for (m = ctx->mem_descs; m != NULL; m = m->next)
604 		if (m->mem_id == d->reg.nr_mem_id)
605 			break;
606 
607 	if (m == NULL) {
608 		m = nmctx_malloc(ctx, sizeof(*m));
609 		if (m == NULL) {
610 			nmctx_ferror(ctx, "cannot allocate memory descriptor");
611 			goto err;
612 		}
613 		memset(m, 0, sizeof(*m));
614 		if (d->extmem != NULL) {
615 			m->mem = (void *)((uintptr_t)d->extmem->nro_usrptr);
616 			m->size = d->extmem->nro_info.nr_memsize;
617 			m->is_extmem = 1;
618 		} else {
619 			m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE,
620 					MAP_SHARED, d->fd, 0);
621 			if (m->mem == MAP_FAILED) {
622 				nmctx_ferror(ctx, "mmap: %s", strerror(errno));
623 				goto err;
624 			}
625 			m->size = d->reg.nr_memsize;
626 		}
627 		m->mem_id = d->reg.nr_mem_id;
628 		m->next = ctx->mem_descs;
629 		if (ctx->mem_descs != NULL)
630 			ctx->mem_descs->prev = m;
631 		ctx->mem_descs = m;
632 	}
633 	m->refcount++;
634 
635 	nmctx_unlock(ctx);
636 
637 	d->mem = m;
638 
639 	d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset);
640 
641 	num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
642 	for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
643 		;
644 	d->first_tx_ring = i;
645 	for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
646 		;
647 	d->last_tx_ring = i - 1;
648 
649 	num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
650 	for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
651 		;
652 	d->first_rx_ring = i;
653 	for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
654 		;
655 	d->last_rx_ring = i - 1;
656 
657 	d->mmap_done = 1;
658 
659 	return 0;
660 
661 err:
662 	nmctx_unlock(ctx);
663 	nmport_undo_mmap(d);
664 	return -1;
665 }
666 
667 void
nmport_undo_mmap(struct nmport_d * d)668 nmport_undo_mmap(struct nmport_d *d)
669 {
670 	struct nmem_d *m;
671 	struct nmctx *ctx = d->ctx;
672 
673 	m = d->mem;
674 	if (m == NULL)
675 		return;
676 	nmctx_lock(ctx);
677 	m->refcount--;
678 	if (m->refcount <= 0) {
679 		if (!m->is_extmem && m->mem != MAP_FAILED)
680 			munmap(m->mem, m->size);
681 		/* extract from the list and free */
682 		if (m->next != NULL)
683 			m->next->prev = m->prev;
684 		if (m->prev != NULL)
685 			m->prev->next = m->next;
686 		else
687 			ctx->mem_descs = m->next;
688 		nmctx_free(ctx, m);
689 		d->mem = NULL;
690 	}
691 	nmctx_unlock(ctx);
692 	d->mmap_done = 0;
693 	d->mem = NULL;
694 	d->nifp = NULL;
695 	d->first_tx_ring = 0;
696 	d->last_tx_ring = 0;
697 	d->first_rx_ring = 0;
698 	d->last_rx_ring = 0;
699 	d->cur_tx_ring = 0;
700 	d->cur_rx_ring = 0;
701 }
702 
703 int
nmport_open_desc(struct nmport_d * d)704 nmport_open_desc(struct nmport_d *d)
705 {
706 	if (nmport_register(d) < 0)
707 		goto err;
708 
709 	if (nmport_mmap(d) < 0)
710 		goto err;
711 
712 	return 0;
713 err:
714 	nmport_undo_open_desc(d);
715 	return -1;
716 }
717 
718 void
nmport_undo_open_desc(struct nmport_d * d)719 nmport_undo_open_desc(struct nmport_d *d)
720 {
721 	nmport_undo_mmap(d);
722 	nmport_undo_register(d);
723 }
724 
725 
726 struct nmport_d *
nmport_open(const char * ifname)727 nmport_open(const char *ifname)
728 {
729 	struct nmport_d *d;
730 
731 	/* prepare the descriptor */
732 	d = nmport_prepare(ifname);
733 	if (d == NULL)
734 		goto err;
735 
736 	/* open netmap and register */
737 	if (nmport_open_desc(d) < 0)
738 		goto err;
739 
740 	return d;
741 
742 err:
743 	nmport_close(d);
744 	return NULL;
745 }
746 
747 void
nmport_close(struct nmport_d * d)748 nmport_close(struct nmport_d *d)
749 {
750 	if (d == NULL)
751 		return;
752 	nmport_undo_open_desc(d);
753 	nmport_undo_prepare(d);
754 }
755 
756 struct nmport_d *
nmport_clone(struct nmport_d * d)757 nmport_clone(struct nmport_d *d)
758 {
759 	struct nmport_d *c;
760 	struct nmctx *ctx;
761 
762 	ctx = d->ctx;
763 
764 	if (d->extmem != NULL && !d->register_done) {
765 		errno = EINVAL;
766 		nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem");
767 		return NULL;
768 	}
769 
770 	c = nmport_new_with_ctx(ctx);
771 	if (c == NULL)
772 		return NULL;
773 	/* copy the output of parse */
774 	c->hdr = d->hdr;
775 	/* redirect the pointer to the body */
776 	c->hdr.nr_body = (uintptr_t)&c->reg;
777 	/* options are not cloned */
778 	c->hdr.nr_options = 0;
779 	c->reg = d->reg; /* this also copies the mem_id */
780 	/* put the new port in an un-registered, unmapped state */
781 	c->fd = -1;
782 	c->nifp = NULL;
783 	c->register_done = 0;
784 	c->mem = NULL;
785 	c->extmem = NULL;
786 	c->mmap_done = 0;
787 	c->first_tx_ring = 0;
788 	c->last_tx_ring = 0;
789 	c->first_rx_ring = 0;
790 	c->last_rx_ring = 0;
791 	c->cur_tx_ring = 0;
792 	c->cur_rx_ring = 0;
793 
794 	return c;
795 }
796 
797 int
nmport_inject(struct nmport_d * d,const void * buf,size_t size)798 nmport_inject(struct nmport_d *d, const void *buf, size_t size)
799 {
800 	u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
801 		ri = d->cur_tx_ring;
802 
803 	for (c = 0; c < n ; c++, ri++) {
804 		/* compute current ring to use */
805 		struct netmap_ring *ring;
806 		uint32_t i, j, idx;
807 		size_t rem;
808 
809 		if (ri > d->last_tx_ring)
810 			ri = d->first_tx_ring;
811 		ring = NETMAP_TXRING(d->nifp, ri);
812 		rem = size;
813 		j = ring->cur;
814 		while (rem > ring->nr_buf_size && j != ring->tail) {
815 			rem -= ring->nr_buf_size;
816 			j = nm_ring_next(ring, j);
817 		}
818 		if (j == ring->tail && rem > 0)
819 			continue;
820 		i = ring->cur;
821 		while (i != j) {
822 			idx = ring->slot[i].buf_idx;
823 			ring->slot[i].len = ring->nr_buf_size;
824 			ring->slot[i].flags = NS_MOREFRAG;
825 			nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
826 			i = nm_ring_next(ring, i);
827 			buf = (char *)buf + ring->nr_buf_size;
828 		}
829 		idx = ring->slot[i].buf_idx;
830 		ring->slot[i].len = rem;
831 		ring->slot[i].flags = 0;
832 		nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
833 		ring->head = ring->cur = nm_ring_next(ring, i);
834 		d->cur_tx_ring = ri;
835 		return size;
836 	}
837 	return 0; /* fail */
838 }
839