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