2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (C) 2018 Universita` di Pisa
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/types.h>
34 #include <sys/ioctl.h>
44 #include <net/netmap_user.h>
45 #define LIBNETMAP_NOTHREADSAFE
46 #include "libnetmap.h"
48 struct nmport_cleanup_d {
49 struct nmport_cleanup_d *next;
50 void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *);
54 nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c)
61 nmport_pop_cleanup(struct nmport_d *d)
63 struct nmport_cleanup_d *top;
66 d->clist = d->clist->next;
67 (*top->cleanup)(top, d);
68 nmctx_free(d->ctx, top);
71 void nmport_do_cleanup(struct nmport_d *d)
73 while (d->clist != NULL) {
74 nmport_pop_cleanup(d);
78 static struct nmport_d *
79 nmport_new_with_ctx(struct nmctx *ctx)
83 /* allocate a descriptor */
84 d = nmctx_malloc(ctx, sizeof(*d));
86 nmctx_ferror(ctx, "cannot allocate nmport descriptor");
89 memset(d, 0, sizeof(*d));
91 nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg);
103 struct nmctx *ctx = nmctx_get();
104 return nmport_new_with_ctx(ctx);
109 nmport_delete(struct nmport_d *d)
111 nmctx_free(d->ctx, d);
115 nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d)
119 if (d->extmem == NULL)
122 nmreq_remove_option(&d->hdr, &d->extmem->nro_opt);
123 nmctx_free(d->ctx, d->extmem);
129 nmport_extmem(struct nmport_d *d, void *base, size_t size)
131 struct nmctx *ctx = d->ctx;
132 struct nmport_cleanup_d *clnup = NULL;
134 if (d->register_done) {
135 nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name);
140 if (d->extmem != NULL) {
141 nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name);
146 clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup));
148 nmctx_ferror(ctx, "failed to allocate cleanup descriptor");
153 d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem));
154 if (d->extmem == NULL) {
155 nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name);
156 nmctx_free(ctx, clnup);
160 memset(d->extmem, 0, sizeof(*d->extmem));
161 d->extmem->nro_usrptr = (uintptr_t)base;
162 d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
163 d->extmem->nro_info.nr_memsize = size;
164 nmreq_push_option(&d->hdr, &d->extmem->nro_opt);
166 clnup->cleanup = nmport_extmem_cleanup;
167 nmport_push_cleanup(d, clnup);
172 struct nmport_extmem_from_file_cleanup_d {
173 struct nmport_cleanup_d up;
178 void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
181 struct nmport_extmem_from_file_cleanup_d *cc =
182 (struct nmport_extmem_from_file_cleanup_d *)c;
184 munmap(cc->p, cc->size);
188 nmport_extmem_from_file(struct nmport_d *d, const char *fname)
190 struct nmctx *ctx = d->ctx;
194 struct nmport_extmem_from_file_cleanup_d *clnup = NULL;
196 clnup = nmctx_malloc(ctx, sizeof(*clnup));
198 nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
203 fd = open(fname, O_RDWR);
205 nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno));
208 mapsize = lseek(fd, 0, SEEK_END);
210 nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno));
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));
221 clnup->size = mapsize;
222 clnup->up.cleanup = nmport_extmem_from_file_cleanup;
223 nmport_push_cleanup(d, &clnup->up);
225 if (nmport_extmem(d, p, mapsize) < 0)
234 if (clnup->p != MAP_FAILED)
235 nmport_pop_cleanup(d);
237 nmctx_free(ctx, clnup);
242 struct nmreq_pools_info*
243 nmport_extmem_getinfo(struct nmport_d *d)
245 if (d->extmem == NULL)
247 return &d->extmem->nro_info;
250 /* head of the list of options */
251 static struct nmreq_opt_parser *nmport_opt_parsers;
253 #define NPOPT_PARSER(o) nmport_opt_##o##_parser
254 #define NPOPT_DESC(o) nmport_opt_##o##_desc
255 #define NPOPT_NRKEYS(o) (NPOPT_DESC(o).nr_keys)
256 #define NPOPT_DECL(o, f) \
257 static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *); \
258 static struct nmreq_opt_parser NPOPT_DESC(o) = { \
260 .parse = NPOPT_PARSER(o), \
266 static void __attribute__((constructor)) \
267 nmport_opt_##o##_ctor(void) \
269 NPOPT_DESC(o).next = nmport_opt_parsers; \
270 nmport_opt_parsers = &NPOPT_DESC(o); \
272 struct nmport_key_desc {
273 struct nmreq_opt_parser *option;
279 nmport_opt_key_ctor(struct nmport_key_desc *k)
281 struct nmreq_opt_parser *o = k->option;
282 struct nmreq_opt_key *ok;
285 ok = &o->keys[k->id];
288 ok->flags = k->flags;
290 if (ok->flags & NMREQ_OPTK_DEFAULT)
291 o->default_key = ok->id;
293 #define NPKEY_DESC(o, k) nmport_opt_##o##_key_##k##_desc
294 #define NPKEY_ID(o, k) (NPKEY_DESC(o, k).id)
295 #define NPKEY_DECL(o, k, f) \
296 static struct nmport_key_desc NPKEY_DESC(o, k) = { \
297 .option = &NPOPT_DESC(o), \
302 static void __attribute__((constructor)) \
303 nmport_opt_##o##_key_##k##_ctor(void) \
305 nmport_opt_key_ctor(&NPKEY_DESC(o, k)); \
307 #define nmport_key(p, o, k) ((p)->keys[NPKEY_ID(o, k)])
308 #define nmport_defkey(p, o) ((p)->keys[NPOPT_DESC(o).default_key])
311 NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
312 NPOPT_DECL(extmem, 0)
313 NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
314 NPKEY_DECL(extmem, if_num, 0)
315 NPKEY_DECL(extmem, if_size, 0)
316 NPKEY_DECL(extmem, ring_num, 0)
317 NPKEY_DECL(extmem, ring_size, 0)
318 NPKEY_DECL(extmem, buf_num, 0)
319 NPKEY_DECL(extmem, buf_size, 0)
321 NPKEY_DECL(conf, rings, 0)
322 NPKEY_DECL(conf, host_rings, 0)
323 NPKEY_DECL(conf, slots, 0)
324 NPKEY_DECL(conf, tx_rings, 0)
325 NPKEY_DECL(conf, rx_rings, 0)
326 NPKEY_DECL(conf, host_tx_rings, 0)
327 NPKEY_DECL(conf, host_rx_rings, 0)
328 NPKEY_DECL(conf, tx_slots, 0)
329 NPKEY_DECL(conf, rx_slots, 0)
333 NPOPT_PARSER(share)(struct nmreq_parse_ctx *p)
335 struct nmctx *ctx = p->ctx;
336 struct nmport_d *d = p->token;
338 const char *v = nmport_defkey(p, share);
340 mem_id = nmreq_get_mem_id(&v, ctx);
343 if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) {
344 nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"",
345 mem_id, d->reg.nr_mem_id);
349 d->reg.nr_mem_id = mem_id;
354 NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p)
357 struct nmreq_pools_info *pi;
362 if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0)
365 pi = &d->extmem->nro_info;
367 for (i = 0; i < NPOPT_NRKEYS(extmem); i++) {
368 const char *k = p->keys[i];
375 if (i == NPKEY_ID(extmem, if_num)) {
376 pi->nr_if_pool_objtotal = v;
377 } else if (i == NPKEY_ID(extmem, if_size)) {
378 pi->nr_if_pool_objsize = v;
379 } else if (i == NPKEY_ID(extmem, ring_num)) {
380 pi->nr_ring_pool_objtotal = v;
381 } else if (i == NPKEY_ID(extmem, ring_size)) {
382 pi->nr_ring_pool_objsize = v;
383 } else if (i == NPKEY_ID(extmem, buf_num)) {
384 pi->nr_buf_pool_objtotal = v;
385 } else if (i == NPKEY_ID(extmem, buf_size)) {
386 pi->nr_buf_pool_objsize = v;
393 NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
399 if (nmport_key(p, conf, rings) != NULL) {
400 uint16_t nr_rings = atoi(nmport_key(p, conf, rings));
401 d->reg.nr_tx_rings = nr_rings;
402 d->reg.nr_rx_rings = nr_rings;
404 if (nmport_key(p, conf, host_rings) != NULL) {
405 uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings));
406 d->reg.nr_host_tx_rings = nr_rings;
407 d->reg.nr_host_rx_rings = nr_rings;
409 if (nmport_key(p, conf, slots) != NULL) {
410 uint32_t nr_slots = atoi(nmport_key(p, conf, slots));
411 d->reg.nr_tx_slots = nr_slots;
412 d->reg.nr_rx_slots = nr_slots;
414 if (nmport_key(p, conf, tx_rings) != NULL) {
415 d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings));
417 if (nmport_key(p, conf, rx_rings) != NULL) {
418 d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings));
420 if (nmport_key(p, conf, host_tx_rings) != NULL) {
421 d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings));
423 if (nmport_key(p, conf, host_rx_rings) != NULL) {
424 d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings));
426 if (nmport_key(p, conf, tx_slots) != NULL) {
427 d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots));
429 if (nmport_key(p, conf, rx_slots) != NULL) {
430 d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots));
436 nmport_disable_option(const char *opt)
438 struct nmreq_opt_parser *p;
440 for (p = nmport_opt_parsers; p != NULL; p = p->next) {
441 if (!strcmp(p->prefix, opt)) {
442 p->flags |= NMREQ_OPTF_DISABLED;
448 nmport_enable_option(const char *opt)
450 struct nmreq_opt_parser *p;
452 for (p = nmport_opt_parsers; p != NULL; p = p->next) {
453 if (!strcmp(p->prefix, opt)) {
454 p->flags &= ~NMREQ_OPTF_DISABLED;
464 nmport_parse(struct nmport_d *d, const char *ifname)
466 const char *scan = ifname;
468 if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) {
472 /* parse the register request */
473 if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) {
477 /* parse the options, if any */
478 if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) {
484 nmport_undo_parse(d);
489 nmport_undo_parse(struct nmport_d *d)
491 nmport_do_cleanup(d);
492 memset(&d->reg, 0, sizeof(d->reg));
493 memset(&d->hdr, 0, sizeof(d->hdr));
497 nmport_prepare(const char *ifname)
501 /* allocate a descriptor */
506 /* parse the header */
507 if (nmport_parse(d, ifname) < 0)
513 nmport_undo_prepare(d);
518 nmport_undo_prepare(struct nmport_d *d)
522 nmport_undo_parse(d);
527 nmport_register(struct nmport_d *d)
529 struct nmctx *ctx = d->ctx;
531 if (d->register_done) {
533 nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name);
537 d->fd = open("/dev/netmap", O_RDWR);
539 nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno));
543 if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) {
544 struct nmreq_option *o;
545 int option_errors = 0;
547 nmreq_foreach_option(&d->hdr, o) {
549 nmctx_ferror(ctx, "%s: option %s: %s",
551 nmreq_option_name(o->nro_reqtype),
552 strerror(o->nro_status));
558 nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno));
562 d->register_done = 1;
567 nmport_undo_register(d);
572 nmport_undo_register(struct nmport_d *d)
577 d->register_done = 0;
580 /* lookup the mem_id in the mem-list: do a new mmap() if
581 * not found, reuse existing otherwise
584 nmport_mmap(struct nmport_d *d)
586 struct nmctx *ctx = d->ctx;
587 struct nmem_d *m = NULL;
588 u_int num_tx, num_rx;
593 nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name);
597 if (!d->register_done) {
599 nmctx_ferror(ctx, "cannot map unregistered port");
605 for (m = ctx->mem_descs; m != NULL; m = m->next)
606 if (m->mem_id == d->reg.nr_mem_id)
610 m = nmctx_malloc(ctx, sizeof(*m));
612 nmctx_ferror(ctx, "cannot allocate memory descriptor");
615 memset(m, 0, sizeof(*m));
616 if (d->extmem != NULL) {
617 m->mem = (void *)d->extmem->nro_usrptr;
618 m->size = d->extmem->nro_info.nr_memsize;
621 m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE,
622 MAP_SHARED, d->fd, 0);
623 if (m->mem == MAP_FAILED) {
624 nmctx_ferror(ctx, "mmap: %s", strerror(errno));
627 m->size = d->reg.nr_memsize;
629 m->mem_id = d->reg.nr_mem_id;
630 m->next = ctx->mem_descs;
631 if (ctx->mem_descs != NULL)
632 ctx->mem_descs->prev = m;
641 d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset);
643 num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
644 for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
646 d->first_tx_ring = i;
647 for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
649 d->last_tx_ring = i - 1;
651 num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
652 for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
654 d->first_rx_ring = i;
655 for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
657 d->last_rx_ring = i - 1;
670 nmport_undo_mmap(struct nmport_d *d)
673 struct nmctx *ctx = d->ctx;
680 if (m->refcount <= 0) {
681 if (!m->is_extmem && m->mem != MAP_FAILED)
682 munmap(m->mem, m->size);
683 /* extract from the list and free */
685 m->next->prev = m->prev;
687 m->prev->next = m->next;
689 ctx->mem_descs = m->next;
697 d->first_tx_ring = 0;
699 d->first_rx_ring = 0;
706 nmport_open_desc(struct nmport_d *d)
708 if (nmport_register(d) < 0)
711 if (nmport_mmap(d) < 0)
716 nmport_undo_open_desc(d);
721 nmport_undo_open_desc(struct nmport_d *d)
724 nmport_undo_register(d);
729 nmport_open(const char *ifname)
733 /* prepare the descriptor */
734 d = nmport_prepare(ifname);
738 /* open netmap and register */
739 if (nmport_open_desc(d) < 0)
750 nmport_close(struct nmport_d *d)
754 nmport_undo_open_desc(d);
755 nmport_undo_prepare(d);
759 nmport_clone(struct nmport_d *d)
766 if (d->extmem != NULL && !d->register_done) {
768 nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem");
772 c = nmport_new_with_ctx(ctx);
775 /* copy the output of parse */
777 /* redirect the pointer to the body */
778 c->hdr.nr_body = (uintptr_t)&c->reg;
779 /* options are not cloned */
780 c->hdr.nr_options = 0;
781 c->reg = d->reg; /* this also copies the mem_id */
782 /* put the new port in an un-registered, unmapped state */
785 c->register_done = 0;
789 c->first_tx_ring = 0;
791 c->first_rx_ring = 0;
800 nmport_inject(struct nmport_d *d, const void *buf, size_t size)
802 u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
805 for (c = 0; c < n ; c++, ri++) {
806 /* compute current ring to use */
807 struct netmap_ring *ring;
811 if (ri > d->last_tx_ring)
812 ri = d->first_tx_ring;
813 ring = NETMAP_TXRING(d->nifp, ri);
816 while (rem > ring->nr_buf_size && j != ring->tail) {
817 rem -= ring->nr_buf_size;
818 j = nm_ring_next(ring, j);
820 if (j == ring->tail && rem > 0)
824 idx = ring->slot[i].buf_idx;
825 ring->slot[i].len = ring->nr_buf_size;
826 ring->slot[i].flags = NS_MOREFRAG;
827 nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
828 i = nm_ring_next(ring, i);
829 buf = (char *)buf + ring->nr_buf_size;
831 idx = ring->slot[i].buf_idx;
832 ring->slot[i].len = rem;
833 ring->slot[i].flags = 0;
834 nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
835 ring->head = ring->cur = nm_ring_next(ring, i);