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