Index: head/lib/libnetmap/nmctx-pthreads.c =================================================================== --- head/lib/libnetmap/nmctx-pthreads.c (revision 365022) +++ head/lib/libnetmap/nmctx-pthreads.c (revision 365023) @@ -1,47 +1,77 @@ -/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2018 Universita` di Pisa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libnetmap.h" struct nmctx_pthread { struct nmctx up; pthread_mutex_t mutex; }; static struct nmctx_pthread nmctx_pthreadsafe; static void nmctx_pthread_lock(struct nmctx *ctx, int lock) { struct nmctx_pthread *ctxp = (struct nmctx_pthread *)ctx; if (lock) { pthread_mutex_lock(&ctxp->mutex); } else { pthread_mutex_unlock(&ctxp->mutex); } } void __attribute__ ((constructor)) nmctx_set_threadsafe(void) { struct nmctx *old; pthread_mutex_init(&nmctx_pthreadsafe.mutex, NULL); old = nmctx_set_default(&nmctx_pthreadsafe.up); nmctx_pthreadsafe.up = *old; nmctx_pthreadsafe.up.lock = nmctx_pthread_lock; } int nmctx_threadsafe; Index: head/lib/libnetmap/nmctx.c =================================================================== --- head/lib/libnetmap/nmctx.c (revision 365022) +++ head/lib/libnetmap/nmctx.c (revision 365023) @@ -1,111 +1,141 @@ -/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2018 Universita` di Pisa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + #include #include #include #include #include #include #include #include #include #include #include #include #define LIBNETMAP_NOTHREADSAFE #include "libnetmap.h" static void nmctx_default_error(struct nmctx *ctx, const char *errmsg) { fprintf(stderr, "%s\n", errmsg); } static void * nmctx_default_malloc(struct nmctx *ctx, size_t sz) { (void)ctx; return malloc(sz); } static void nmctx_default_free(struct nmctx *ctx, void *p) { (void)ctx; free(p); } static struct nmctx nmctx_global = { .verbose = 1, .error = nmctx_default_error, .malloc = nmctx_default_malloc, .free = nmctx_default_free, .lock = NULL, }; static struct nmctx *nmctx_default = &nmctx_global; struct nmctx * nmctx_get(void) { return nmctx_default; } struct nmctx * nmctx_set_default(struct nmctx *ctx) { struct nmctx *old = nmctx_default; nmctx_default = ctx; return old; } #define MAXERRMSG 1000 void nmctx_ferror(struct nmctx *ctx, const char *fmt, ...) { char errmsg[MAXERRMSG]; va_list ap; int rv; if (!ctx->verbose) return; va_start(ap, fmt); rv = vsnprintf(errmsg, MAXERRMSG, fmt, ap); va_end(ap); if (rv > 0) { if (rv < MAXERRMSG) { ctx->error(ctx, errmsg); } else { ctx->error(ctx, "error message too long"); } } else { ctx->error(ctx, "internal error"); } } void * nmctx_malloc(struct nmctx *ctx, size_t sz) { return ctx->malloc(ctx, sz); } void nmctx_free(struct nmctx *ctx, void *p) { ctx->free(ctx, p); } void nmctx_lock(struct nmctx *ctx) { if (ctx->lock != NULL) ctx->lock(ctx, 1); } void nmctx_unlock(struct nmctx *ctx) { if (ctx->lock != NULL) ctx->lock(ctx, 0); } Index: head/lib/libnetmap/nmport.c =================================================================== --- head/lib/libnetmap/nmport.c (revision 365022) +++ head/lib/libnetmap/nmport.c (revision 365023) @@ -1,810 +1,840 @@ -/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2018 Universita` di Pisa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + #include #include #include #include #include #include #include #include #include #include #include #include #include #define LIBNETMAP_NOTHREADSAFE #include "libnetmap.h" struct nmport_cleanup_d { struct nmport_cleanup_d *next; void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *); }; static void nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c) { c->next = d->clist; d->clist = c; } static void nmport_pop_cleanup(struct nmport_d *d) { struct nmport_cleanup_d *top; top = d->clist; d->clist = d->clist->next; (*top->cleanup)(top, d); nmctx_free(d->ctx, top); } void nmport_do_cleanup(struct nmport_d *d) { while (d->clist != NULL) { nmport_pop_cleanup(d); } } static struct nmport_d * nmport_new_with_ctx(struct nmctx *ctx) { struct nmport_d *d; /* allocate a descriptor */ d = nmctx_malloc(ctx, sizeof(*d)); if (d == NULL) { nmctx_ferror(ctx, "cannot allocate nmport descriptor"); goto out; } memset(d, 0, sizeof(*d)); nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg); d->ctx = ctx; d->fd = -1; out: return d; } struct nmport_d * nmport_new(void) { struct nmctx *ctx = nmctx_get(); return nmport_new_with_ctx(ctx); } void nmport_delete(struct nmport_d *d) { nmctx_free(d->ctx, d); } void nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d) { (void)c; if (d->extmem == NULL) return; nmreq_remove_option(&d->hdr, &d->extmem->nro_opt); nmctx_free(d->ctx, d->extmem); d->extmem = NULL; } int nmport_extmem(struct nmport_d *d, void *base, size_t size) { struct nmctx *ctx = d->ctx; struct nmport_cleanup_d *clnup = NULL; if (d->register_done) { nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name); errno = EINVAL; return -1; } if (d->extmem != NULL) { nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name); errno = EINVAL; return -1; } clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup)); if (clnup == NULL) { nmctx_ferror(ctx, "failed to allocate cleanup descriptor"); errno = ENOMEM; return -1; } d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem)); if (d->extmem == NULL) { nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name); nmctx_free(ctx, clnup); errno = ENOMEM; return -1; } memset(d->extmem, 0, sizeof(*d->extmem)); d->extmem->nro_usrptr = (uintptr_t)base; d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM; d->extmem->nro_info.nr_memsize = size; nmreq_push_option(&d->hdr, &d->extmem->nro_opt); clnup->cleanup = nmport_extmem_cleanup; nmport_push_cleanup(d, clnup); return 0; } struct nmport_extmem_from_file_cleanup_d { struct nmport_cleanup_d up; void *p; size_t size; }; void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d) { struct nmport_extmem_from_file_cleanup_d *cc = (struct nmport_extmem_from_file_cleanup_d *)c; munmap(cc->p, cc->size); } int nmport_extmem_from_file(struct nmport_d *d, const char *fname) { struct nmctx *ctx = d->ctx; int fd = -1; off_t mapsize; void *p; struct nmport_extmem_from_file_cleanup_d *clnup = NULL; clnup = nmctx_malloc(ctx, sizeof(*clnup)); if (clnup == NULL) { nmctx_ferror(ctx, "cannot allocate cleanup descriptor"); errno = ENOMEM; goto fail; } fd = open(fname, O_RDWR); if (fd < 0) { nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno)); goto fail; } mapsize = lseek(fd, 0, SEEK_END); if (mapsize < 0) { nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno)); goto fail; } p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno)); goto fail; } close(fd); clnup->p = p; clnup->size = mapsize; clnup->up.cleanup = nmport_extmem_from_file_cleanup; nmport_push_cleanup(d, &clnup->up); if (nmport_extmem(d, p, mapsize) < 0) goto fail; return 0; fail: if (fd >= 0) close(fd); if (clnup != NULL) { if (clnup->p != MAP_FAILED) nmport_pop_cleanup(d); else nmctx_free(ctx, clnup); } return -1; } struct nmreq_pools_info* nmport_extmem_getinfo(struct nmport_d *d) { if (d->extmem == NULL) return NULL; return &d->extmem->nro_info; } /* head of the list of options */ static struct nmreq_opt_parser *nmport_opt_parsers; #define NPOPT_PARSER(o) nmport_opt_##o##_parser #define NPOPT_DESC(o) nmport_opt_##o##_desc #define NPOPT_NRKEYS(o) (NPOPT_DESC(o).nr_keys) #define NPOPT_DECL(o, f) \ static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *); \ static struct nmreq_opt_parser NPOPT_DESC(o) = { \ .prefix = #o, \ .parse = NPOPT_PARSER(o), \ .flags = (f), \ .default_key = -1, \ .nr_keys = 0, \ .next = NULL, \ }; \ static void __attribute__((constructor)) \ nmport_opt_##o##_ctor(void) \ { \ NPOPT_DESC(o).next = nmport_opt_parsers; \ nmport_opt_parsers = &NPOPT_DESC(o); \ } struct nmport_key_desc { struct nmreq_opt_parser *option; const char *key; unsigned int flags; int id; }; static void nmport_opt_key_ctor(struct nmport_key_desc *k) { struct nmreq_opt_parser *o = k->option; struct nmreq_opt_key *ok; k->id = o->nr_keys; ok = &o->keys[k->id]; ok->key = k->key; ok->id = k->id; ok->flags = k->flags; o->nr_keys++; if (ok->flags & NMREQ_OPTK_DEFAULT) o->default_key = ok->id; } #define NPKEY_DESC(o, k) nmport_opt_##o##_key_##k##_desc #define NPKEY_ID(o, k) (NPKEY_DESC(o, k).id) #define NPKEY_DECL(o, k, f) \ static struct nmport_key_desc NPKEY_DESC(o, k) = { \ .option = &NPOPT_DESC(o), \ .key = #k, \ .flags = (f), \ .id = -1, \ }; \ static void __attribute__((constructor)) \ nmport_opt_##o##_key_##k##_ctor(void) \ { \ nmport_opt_key_ctor(&NPKEY_DESC(o, k)); \ } #define nmport_key(p, o, k) ((p)->keys[NPKEY_ID(o, k)]) #define nmport_defkey(p, o) ((p)->keys[NPOPT_DESC(o).default_key]) NPOPT_DECL(share, 0) NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET) NPOPT_DECL(extmem, 0) NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET) NPKEY_DECL(extmem, if_num, 0) NPKEY_DECL(extmem, if_size, 0) NPKEY_DECL(extmem, ring_num, 0) NPKEY_DECL(extmem, ring_size, 0) NPKEY_DECL(extmem, buf_num, 0) NPKEY_DECL(extmem, buf_size, 0) NPOPT_DECL(conf, 0) NPKEY_DECL(conf, rings, 0) NPKEY_DECL(conf, host_rings, 0) NPKEY_DECL(conf, slots, 0) NPKEY_DECL(conf, tx_rings, 0) NPKEY_DECL(conf, rx_rings, 0) NPKEY_DECL(conf, host_tx_rings, 0) NPKEY_DECL(conf, host_rx_rings, 0) NPKEY_DECL(conf, tx_slots, 0) NPKEY_DECL(conf, rx_slots, 0) static int NPOPT_PARSER(share)(struct nmreq_parse_ctx *p) { struct nmctx *ctx = p->ctx; struct nmport_d *d = p->token; int32_t mem_id; const char *v = nmport_defkey(p, share); mem_id = nmreq_get_mem_id(&v, ctx); if (mem_id < 0) return -1; if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) { nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"", mem_id, d->reg.nr_mem_id); errno = EINVAL; return -1; } d->reg.nr_mem_id = mem_id; return 0; } static int NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p) { struct nmport_d *d; struct nmreq_pools_info *pi; int i; d = p->token; if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0) return -1; pi = &d->extmem->nro_info; for (i = 0; i < NPOPT_NRKEYS(extmem); i++) { const char *k = p->keys[i]; uint32_t v; if (k == NULL) continue; v = atoi(k); if (i == NPKEY_ID(extmem, if_num)) { pi->nr_if_pool_objtotal = v; } else if (i == NPKEY_ID(extmem, if_size)) { pi->nr_if_pool_objsize = v; } else if (i == NPKEY_ID(extmem, ring_num)) { pi->nr_ring_pool_objtotal = v; } else if (i == NPKEY_ID(extmem, ring_size)) { pi->nr_ring_pool_objsize = v; } else if (i == NPKEY_ID(extmem, buf_num)) { pi->nr_buf_pool_objtotal = v; } else if (i == NPKEY_ID(extmem, buf_size)) { pi->nr_buf_pool_objsize = v; } } return 0; } static int NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p) { struct nmport_d *d; d = p->token; if (nmport_key(p, conf, rings) != NULL) { uint16_t nr_rings = atoi(nmport_key(p, conf, rings)); d->reg.nr_tx_rings = nr_rings; d->reg.nr_rx_rings = nr_rings; } if (nmport_key(p, conf, host_rings) != NULL) { uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings)); d->reg.nr_host_tx_rings = nr_rings; d->reg.nr_host_rx_rings = nr_rings; } if (nmport_key(p, conf, slots) != NULL) { uint32_t nr_slots = atoi(nmport_key(p, conf, slots)); d->reg.nr_tx_slots = nr_slots; d->reg.nr_rx_slots = nr_slots; } if (nmport_key(p, conf, tx_rings) != NULL) { d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings)); } if (nmport_key(p, conf, rx_rings) != NULL) { d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings)); } if (nmport_key(p, conf, host_tx_rings) != NULL) { d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings)); } if (nmport_key(p, conf, host_rx_rings) != NULL) { d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings)); } if (nmport_key(p, conf, tx_slots) != NULL) { d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots)); } if (nmport_key(p, conf, rx_slots) != NULL) { d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots)); } return 0; } void nmport_disable_option(const char *opt) { struct nmreq_opt_parser *p; for (p = nmport_opt_parsers; p != NULL; p = p->next) { if (!strcmp(p->prefix, opt)) { p->flags |= NMREQ_OPTF_DISABLED; } } } int nmport_enable_option(const char *opt) { struct nmreq_opt_parser *p; for (p = nmport_opt_parsers; p != NULL; p = p->next) { if (!strcmp(p->prefix, opt)) { p->flags &= ~NMREQ_OPTF_DISABLED; return 0; } } errno = EOPNOTSUPP; return -1; } int nmport_parse(struct nmport_d *d, const char *ifname) { const char *scan = ifname; if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) { goto err; } /* parse the register request */ if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) { goto err; } /* parse the options, if any */ if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) { goto err; } return 0; err: nmport_undo_parse(d); return -1; } void nmport_undo_parse(struct nmport_d *d) { nmport_do_cleanup(d); memset(&d->reg, 0, sizeof(d->reg)); memset(&d->hdr, 0, sizeof(d->hdr)); } struct nmport_d * nmport_prepare(const char *ifname) { struct nmport_d *d; /* allocate a descriptor */ d = nmport_new(); if (d == NULL) goto err; /* parse the header */ if (nmport_parse(d, ifname) < 0) goto err; return d; err: nmport_undo_prepare(d); return NULL; } void nmport_undo_prepare(struct nmport_d *d) { if (d == NULL) return; nmport_undo_parse(d); nmport_delete(d); } int nmport_register(struct nmport_d *d) { struct nmctx *ctx = d->ctx; if (d->register_done) { errno = EINVAL; nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name); return -1; } d->fd = open("/dev/netmap", O_RDWR); if (d->fd < 0) { nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno)); goto err; } if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) { struct nmreq_option *o; int option_errors = 0; nmreq_foreach_option(&d->hdr, o) { if (o->nro_status) { nmctx_ferror(ctx, "%s: option %s: %s", d->hdr.nr_name, nmreq_option_name(o->nro_reqtype), strerror(o->nro_status)); option_errors++; } } if (!option_errors) nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno)); goto err; } d->register_done = 1; return 0; err: nmport_undo_register(d); return -1; } void nmport_undo_register(struct nmport_d *d) { if (d->fd >= 0) close(d->fd); d->fd = -1; d->register_done = 0; } /* lookup the mem_id in the mem-list: do a new mmap() if * not found, reuse existing otherwise */ int nmport_mmap(struct nmport_d *d) { struct nmctx *ctx = d->ctx; struct nmem_d *m = NULL; u_int num_tx, num_rx; int i; if (d->mmap_done) { errno = EINVAL; nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name); return -1; } if (!d->register_done) { errno = EINVAL; nmctx_ferror(ctx, "cannot map unregistered port"); return -1; } nmctx_lock(ctx); for (m = ctx->mem_descs; m != NULL; m = m->next) if (m->mem_id == d->reg.nr_mem_id) break; if (m == NULL) { m = nmctx_malloc(ctx, sizeof(*m)); if (m == NULL) { nmctx_ferror(ctx, "cannot allocate memory descriptor"); goto err; } memset(m, 0, sizeof(*m)); if (d->extmem != NULL) { m->mem = (void *)d->extmem->nro_usrptr; m->size = d->extmem->nro_info.nr_memsize; m->is_extmem = 1; } else { m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE, MAP_SHARED, d->fd, 0); if (m->mem == MAP_FAILED) { nmctx_ferror(ctx, "mmap: %s", strerror(errno)); goto err; } m->size = d->reg.nr_memsize; } m->mem_id = d->reg.nr_mem_id; m->next = ctx->mem_descs; if (ctx->mem_descs != NULL) ctx->mem_descs->prev = m; ctx->mem_descs = m; } m->refcount++; nmctx_unlock(ctx); d->mem = m; d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset); num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings; for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++) ; d->first_tx_ring = i; for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++) ; d->last_tx_ring = i - 1; num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings; for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++) ; d->first_rx_ring = i; for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++) ; d->last_rx_ring = i - 1; d->mmap_done = 1; return 0; err: nmctx_unlock(ctx); nmport_undo_mmap(d); return -1; } void nmport_undo_mmap(struct nmport_d *d) { struct nmem_d *m; struct nmctx *ctx = d->ctx; m = d->mem; if (m == NULL) return; nmctx_lock(ctx); m->refcount--; if (m->refcount <= 0) { if (!m->is_extmem && m->mem != MAP_FAILED) munmap(m->mem, m->size); /* extract from the list and free */ if (m->next != NULL) m->next->prev = m->prev; if (m->prev != NULL) m->prev->next = m->next; else ctx->mem_descs = m->next; nmctx_free(ctx, m); d->mem = NULL; } nmctx_unlock(ctx); d->mmap_done = 0; d->mem = NULL; d->nifp = NULL; d->first_tx_ring = 0; d->last_tx_ring = 0; d->first_rx_ring = 0; d->last_rx_ring = 0; d->cur_tx_ring = 0; d->cur_rx_ring = 0; } int nmport_open_desc(struct nmport_d *d) { if (nmport_register(d) < 0) goto err; if (nmport_mmap(d) < 0) goto err; return 0; err: nmport_undo_open_desc(d); return -1; } void nmport_undo_open_desc(struct nmport_d *d) { nmport_undo_mmap(d); nmport_undo_register(d); } struct nmport_d * nmport_open(const char *ifname) { struct nmport_d *d; /* prepare the descriptor */ d = nmport_prepare(ifname); if (d == NULL) goto err; /* open netmap and register */ if (nmport_open_desc(d) < 0) goto err; return d; err: nmport_close(d); return NULL; } void nmport_close(struct nmport_d *d) { if (d == NULL) return; nmport_undo_open_desc(d); nmport_undo_prepare(d); } struct nmport_d * nmport_clone(struct nmport_d *d) { struct nmport_d *c; struct nmctx *ctx; ctx = d->ctx; if (d->extmem != NULL && !d->register_done) { errno = EINVAL; nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem"); return NULL; } c = nmport_new_with_ctx(ctx); if (c == NULL) return NULL; /* copy the output of parse */ c->hdr = d->hdr; /* redirect the pointer to the body */ c->hdr.nr_body = (uintptr_t)&c->reg; /* options are not cloned */ c->hdr.nr_options = 0; c->reg = d->reg; /* this also copies the mem_id */ /* put the new port in an un-registered, unmapped state */ c->fd = -1; c->nifp = NULL; c->register_done = 0; c->mem = NULL; c->extmem = NULL; c->mmap_done = 0; c->first_tx_ring = 0; c->last_tx_ring = 0; c->first_rx_ring = 0; c->last_rx_ring = 0; c->cur_tx_ring = 0; c->cur_rx_ring = 0; return c; } int nmport_inject(struct nmport_d *d, const void *buf, size_t size) { u_int c, n = d->last_tx_ring - d->first_tx_ring + 1, ri = d->cur_tx_ring; for (c = 0; c < n ; c++, ri++) { /* compute current ring to use */ struct netmap_ring *ring; uint32_t i, j, idx; size_t rem; if (ri > d->last_tx_ring) ri = d->first_tx_ring; ring = NETMAP_TXRING(d->nifp, ri); rem = size; j = ring->cur; while (rem > ring->nr_buf_size && j != ring->tail) { rem -= ring->nr_buf_size; j = nm_ring_next(ring, j); } if (j == ring->tail && rem > 0) continue; i = ring->cur; while (i != j) { idx = ring->slot[i].buf_idx; ring->slot[i].len = ring->nr_buf_size; ring->slot[i].flags = NS_MOREFRAG; nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size); i = nm_ring_next(ring, i); buf = (char *)buf + ring->nr_buf_size; } idx = ring->slot[i].buf_idx; ring->slot[i].len = rem; ring->slot[i].flags = 0; nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem); ring->head = ring->cur = nm_ring_next(ring, i); d->cur_tx_ring = ri; return size; } return 0; /* fail */ } Index: head/lib/libnetmap/nmreq.c =================================================================== --- head/lib/libnetmap/nmreq.c (revision 365022) +++ head/lib/libnetmap/nmreq.c (revision 365023) @@ -1,684 +1,714 @@ -/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2018 Universita` di Pisa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + #include #include #include #include #include #include #include #include #include #include #include #include #include //#define NMREQ_DEBUG #ifdef NMREQ_DEBUG #define NETMAP_WITH_LIBS #define ED(...) D(__VA_ARGS__) #else #define ED(...) /* an identifier is a possibly empty sequence of alphanum characters and * underscores */ static int nm_is_identifier(const char *s, const char *e) { for (; s != e; s++) { if (!isalnum(*s) && *s != '_') { return 0; } } return 1; } #endif /* NMREQ_DEBUG */ #include #define LIBNETMAP_NOTHREADSAFE #include "libnetmap.h" void nmreq_push_option(struct nmreq_header *h, struct nmreq_option *o) { o->nro_next = h->nr_options; h->nr_options = (uintptr_t)o; } struct nmreq_prefix { const char *prefix; /* the constant part of the prefix */ size_t len; /* its strlen() */ uint32_t flags; #define NR_P_ID (1U << 0) /* whether an identifier is needed */ #define NR_P_SKIP (1U << 1) /* whether the scope must be passed to netmap */ #define NR_P_EMPTYID (1U << 2) /* whether an empty identifier is allowed */ }; #define declprefix(prefix, flags) { (prefix), (sizeof(prefix) - 1), (flags) } static struct nmreq_prefix nmreq_prefixes[] = { declprefix("netmap", NR_P_SKIP), declprefix(NM_BDG_NAME, NR_P_ID|NR_P_EMPTYID), { NULL } /* terminate the list */ }; void nmreq_header_init(struct nmreq_header *h, uint16_t reqtype, void *body) { memset(h, 0, sizeof(*h)); h->nr_version = NETMAP_API; h->nr_reqtype = reqtype; h->nr_body = (uintptr_t)body; } int nmreq_header_decode(const char **pifname, struct nmreq_header *h, struct nmctx *ctx) { const char *scan = NULL; const char *vpname = NULL; const char *pipesep = NULL; u_int namelen; const char *ifname = *pifname; struct nmreq_prefix *p; scan = ifname; for (p = nmreq_prefixes; p->prefix != NULL; p++) { if (!strncmp(scan, p->prefix, p->len)) break; } if (p->prefix == NULL) { nmctx_ferror(ctx, "%s: invalid request, prefix unknown or missing", *pifname); goto fail; } scan += p->len; vpname = index(scan, ':'); if (vpname == NULL) { nmctx_ferror(ctx, "%s: missing ':'", ifname); goto fail; } if (vpname != scan) { /* there is an identifier, can we accept it? */ if (!(p->flags & NR_P_ID)) { nmctx_ferror(ctx, "%s: no identifier allowed between '%s' and ':'", *pifname, p->prefix); goto fail; } if (!nm_is_identifier(scan, vpname)) { nmctx_ferror(ctx, "%s: invalid identifier '%.*s'", *pifname, vpname - scan, scan); goto fail; } } else { if ((p->flags & NR_P_ID) && !(p->flags & NR_P_EMPTYID)) { nmctx_ferror(ctx, "%s: identifier is missing between '%s' and ':'", *pifname, p->prefix); goto fail; } } ++vpname; /* skip the colon */ if (p->flags & NR_P_SKIP) ifname = vpname; scan = vpname; /* scan for a separator */ for (; *scan && !index("-*^/@", *scan); scan++) ; /* search for possible pipe indicators */ for (pipesep = vpname; pipesep != scan && !index("{}", *pipesep); pipesep++) ; if (!nm_is_identifier(vpname, pipesep)) { nmctx_ferror(ctx, "%s: invalid port name '%.*s'", *pifname, pipesep - vpname, vpname); goto fail; } if (pipesep != scan) { pipesep++; if (*pipesep == '\0') { nmctx_ferror(ctx, "%s: invalid empty pipe name", *pifname); goto fail; } if (!nm_is_identifier(pipesep, scan)) { nmctx_ferror(ctx, "%s: invalid pipe name '%.*s'", *pifname, scan - pipesep, pipesep); goto fail; } } namelen = scan - ifname; if (namelen >= sizeof(h->nr_name)) { nmctx_ferror(ctx, "name '%.*s' too long", namelen, ifname); goto fail; } if (namelen == 0) { nmctx_ferror(ctx, "%s: invalid empty port name", *pifname); goto fail; } /* fill the header */ memcpy(h->nr_name, ifname, namelen); h->nr_name[namelen] = '\0'; ED("name %s", h->nr_name); *pifname = scan; return 0; fail: errno = EINVAL; return -1; } /* * 0 not recognized * -1 error * >= 0 mem_id */ int32_t nmreq_get_mem_id(const char **pifname, struct nmctx *ctx) { int fd = -1; struct nmreq_header gh; struct nmreq_port_info_get gb; const char *ifname; errno = 0; ifname = *pifname; if (ifname == NULL) goto fail; /* try to look for a netmap port with this name */ fd = open("/dev/netmap", O_RDWR); if (fd < 0) { nmctx_ferror(ctx, "cannot open /dev/netmap: %s", strerror(errno)); goto fail; } nmreq_header_init(&gh, NETMAP_REQ_PORT_INFO_GET, &gb); if (nmreq_header_decode(&ifname, &gh, ctx) < 0) { goto fail; } memset(&gb, 0, sizeof(gb)); if (ioctl(fd, NIOCCTRL, &gh) < 0) { nmctx_ferror(ctx, "cannot get info for '%s': %s", *pifname, strerror(errno)); goto fail; } *pifname = ifname; close(fd); return gb.nr_mem_id; fail: if (fd >= 0) close(fd); if (!errno) errno = EINVAL; return -1; } int nmreq_register_decode(const char **pifname, struct nmreq_register *r, struct nmctx *ctx) { enum { P_START, P_RNGSFXOK, P_GETNUM, P_FLAGS, P_FLAGSOK, P_MEMID, P_ONESW } p_state; long num; const char *scan = *pifname; uint32_t nr_mode; uint16_t nr_mem_id; uint16_t nr_ringid; uint64_t nr_flags; /* fill the request */ p_state = P_START; /* defaults */ nr_mode = NR_REG_ALL_NIC; /* default for no suffix */ nr_mem_id = r->nr_mem_id; /* if non-zero, further updates are disabled */ nr_ringid = 0; nr_flags = 0; while (*scan) { switch (p_state) { case P_START: switch (*scan) { case '^': /* only SW ring */ nr_mode = NR_REG_SW; p_state = P_ONESW; break; case '*': /* NIC and SW */ nr_mode = NR_REG_NIC_SW; p_state = P_RNGSFXOK; break; case '-': /* one NIC ring pair */ nr_mode = NR_REG_ONE_NIC; p_state = P_GETNUM; break; case '/': /* start of flags */ p_state = P_FLAGS; break; case '@': /* start of memid */ p_state = P_MEMID; break; default: nmctx_ferror(ctx, "unknown modifier: '%c'", *scan); goto fail; } scan++; break; case P_RNGSFXOK: switch (*scan) { case '/': p_state = P_FLAGS; break; case '@': p_state = P_MEMID; break; default: nmctx_ferror(ctx, "unexpected character: '%c'", *scan); goto fail; } scan++; break; case P_GETNUM: if (!isdigit(*scan)) { nmctx_ferror(ctx, "got '%s' while expecting a number", scan); goto fail; } num = strtol(scan, (char **)&scan, 10); if (num < 0 || num >= NETMAP_RING_MASK) { nmctx_ferror(ctx, "'%ld' out of range [0, %d)", num, NETMAP_RING_MASK); goto fail; } nr_ringid = num & NETMAP_RING_MASK; p_state = P_RNGSFXOK; break; case P_FLAGS: case P_FLAGSOK: switch (*scan) { case '@': p_state = P_MEMID; scan++; continue; case 'x': nr_flags |= NR_EXCLUSIVE; break; case 'z': nr_flags |= NR_ZCOPY_MON; break; case 't': nr_flags |= NR_MONITOR_TX; break; case 'r': nr_flags |= NR_MONITOR_RX; break; case 'R': nr_flags |= NR_RX_RINGS_ONLY; break; case 'T': nr_flags |= NR_TX_RINGS_ONLY; break; default: nmctx_ferror(ctx, "unrecognized flag: '%c'", *scan); goto fail; } scan++; p_state = P_FLAGSOK; break; case P_MEMID: if (!isdigit(*scan)) { scan--; /* escape to options */ goto out; } num = strtol(scan, (char **)&scan, 10); if (num <= 0) { nmctx_ferror(ctx, "invalid mem_id: '%ld'", num); goto fail; } if (nr_mem_id && nr_mem_id != num) { nmctx_ferror(ctx, "invalid setting of mem_id to %ld (already set to %"PRIu16")", num, nr_mem_id); goto fail; } nr_mem_id = num; p_state = P_RNGSFXOK; break; case P_ONESW: if (!isdigit(*scan)) { p_state = P_RNGSFXOK; } else { nr_mode = NR_REG_ONE_SW; p_state = P_GETNUM; } break; } } if (p_state == P_MEMID && !*scan) { nmctx_ferror(ctx, "invalid empty mem_id"); goto fail; } if (p_state != P_START && p_state != P_RNGSFXOK && p_state != P_FLAGSOK && p_state != P_MEMID && p_state != P_ONESW) { nmctx_ferror(ctx, "unexpected end of request"); goto fail; } out: ED("flags: %s %s %s %s %s %s", (nr_flags & NR_EXCLUSIVE) ? "EXCLUSIVE" : "", (nr_flags & NR_ZCOPY_MON) ? "ZCOPY_MON" : "", (nr_flags & NR_MONITOR_TX) ? "MONITOR_TX" : "", (nr_flags & NR_MONITOR_RX) ? "MONITOR_RX" : "", (nr_flags & NR_RX_RINGS_ONLY) ? "RX_RINGS_ONLY" : "", (nr_flags & NR_TX_RINGS_ONLY) ? "TX_RINGS_ONLY" : ""); r->nr_mode = nr_mode; r->nr_ringid = nr_ringid; r->nr_flags = nr_flags; r->nr_mem_id = nr_mem_id; *pifname = scan; return 0; fail: if (!errno) errno = EINVAL; return -1; } static int nmreq_option_parsekeys(const char *prefix, char *body, struct nmreq_opt_parser *p, struct nmreq_parse_ctx *pctx) { char *scan; char delim1; struct nmreq_opt_key *k; scan = body; delim1 = *scan; while (delim1 != '\0') { char *key, *value; char delim; size_t vlen; key = scan; for ( scan++; *scan != '\0' && *scan != '=' && *scan != ','; scan++) { if (*scan == '-') *scan = '_'; } delim = *scan; *scan = '\0'; scan++; for (k = p->keys; (k - p->keys) < NMREQ_OPT_MAXKEYS && k->key != NULL; k++) { if (!strcmp(k->key, key)) goto found; } nmctx_ferror(pctx->ctx, "unknown key: '%s'", key); errno = EINVAL; return -1; found: if (pctx->keys[k->id] != NULL) { nmctx_ferror(pctx->ctx, "option '%s': duplicate key '%s', already set to '%s'", prefix, key, pctx->keys[k->id]); errno = EINVAL; return -1; } value = scan; for ( ; *scan != '\0' && *scan != ','; scan++) ; delim1 = *scan; *scan = '\0'; vlen = scan - value; scan++; if (delim == '=') { pctx->keys[k->id] = (vlen ? value : NULL); } else { if (!(k->flags & NMREQ_OPTK_ALLOWEMPTY)) { nmctx_ferror(pctx->ctx, "option '%s': missing '=value' for key '%s'", prefix, key); errno = EINVAL; return -1; } pctx->keys[k->id] = key; } } /* now check that all no-default keys have been assigned */ for (k = p->keys; (k - p->keys) < NMREQ_OPT_MAXKEYS && k->key != NULL; k++) { if ((k->flags & NMREQ_OPTK_MUSTSET) && pctx->keys[k->id] == NULL) { nmctx_ferror(pctx->ctx, "option '%s': mandatory key '%s' not assigned", prefix, k->key); errno = EINVAL; return -1; } } return 0; } static int nmreq_option_decode1(char *opt, struct nmreq_opt_parser *parsers, void *token, struct nmctx *ctx) { struct nmreq_opt_parser *p; const char *prefix; char *scan; char delim; struct nmreq_parse_ctx pctx; int i; prefix = opt; /* find the delimiter */ for (scan = opt; *scan != '\0' && *scan != ':' && *scan != '='; scan++) ; delim = *scan; *scan = '\0'; scan++; /* find the prefix */ for (p = parsers; p != NULL; p = p->next) { if (!strcmp(prefix, p->prefix)) break; } if (p == NULL) { nmctx_ferror(ctx, "unknown option: '%s'", prefix); errno = EINVAL; return -1; } if (p->flags & NMREQ_OPTF_DISABLED) { nmctx_ferror(ctx, "option '%s' is not supported", prefix); errno = EOPNOTSUPP; return -1; } /* prepare the parse context */ pctx.ctx = ctx; pctx.token = token; for (i = 0; i < NMREQ_OPT_MAXKEYS; i++) pctx.keys[i] = NULL; switch (delim) { case '\0': /* no body */ if (!(p->flags & NMREQ_OPTF_ALLOWEMPTY)) { nmctx_ferror(ctx, "syntax error: missing body after '%s'", prefix); errno = EINVAL; return -1; } break; case '=': /* the body goes to the default option key, if any */ if (p->default_key < 0 || p->default_key >= NMREQ_OPT_MAXKEYS) { nmctx_ferror(ctx, "syntax error: '=' not valid after '%s'", prefix); errno = EINVAL; return -1; } if (*scan == '\0') { nmctx_ferror(ctx, "missing value for option '%s'", prefix); errno = EINVAL; return -1; } pctx.keys[p->default_key] = scan; break; case ':': /* parse 'key=value' strings */ if (nmreq_option_parsekeys(prefix, scan, p, &pctx) < 0) return -1; break; } return p->parse(&pctx); } int nmreq_options_decode(const char *opt, struct nmreq_opt_parser parsers[], void *token, struct nmctx *ctx) { const char *scan, *opt1; char *w; size_t len; int ret; if (*opt == '\0') return 0; /* empty list, OK */ if (*opt != '@') { nmctx_ferror(ctx, "option list does not start with '@'"); errno = EINVAL; return -1; } scan = opt; do { scan++; /* skip the plus */ opt1 = scan; /* start of option */ /* find the end of the option */ for ( ; *scan != '\0' && *scan != '@'; scan++) ; len = scan - opt1; if (len == 0) { nmctx_ferror(ctx, "invalid empty option"); errno = EINVAL; return -1; } w = nmctx_malloc(ctx, len + 1); if (w == NULL) { nmctx_ferror(ctx, "out of memory"); errno = ENOMEM; return -1; } memcpy(w, opt1, len); w[len] = '\0'; ret = nmreq_option_decode1(w, parsers, token, ctx); nmctx_free(ctx, w); if (ret < 0) return -1; } while (*scan != '\0'); return 0; } struct nmreq_option * nmreq_find_option(struct nmreq_header *h, uint32_t t) { struct nmreq_option *o; for (o = (struct nmreq_option *)h->nr_options; o != NULL; o = (struct nmreq_option *)o->nro_next) { if (o->nro_reqtype == t) break; } return o; } void nmreq_remove_option(struct nmreq_header *h, struct nmreq_option *o) { struct nmreq_option **nmo; for (nmo = (struct nmreq_option **)&h->nr_options; *nmo != NULL; nmo = (struct nmreq_option **)&(*nmo)->nro_next) { if (*nmo == o) { *((uint64_t *)(*nmo)) = o->nro_next; o->nro_next = (uint64_t)(uintptr_t)NULL; break; } } } void nmreq_free_options(struct nmreq_header *h) { struct nmreq_option *o, *next; for (o = (struct nmreq_option *)h->nr_options; o != NULL; o = next) { next = (struct nmreq_option *)o->nro_next; free(o); } } const char* nmreq_option_name(uint32_t nro_reqtype) { switch (nro_reqtype) { case NETMAP_REQ_OPT_EXTMEM: return "extmem"; case NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS: return "sync-kloop-eventfds"; case NETMAP_REQ_OPT_CSB: return "csb"; case NETMAP_REQ_OPT_SYNC_KLOOP_MODE: return "sync-kloop-mode"; default: return "unknown"; } } #if 0 #include static void nmreq_dump(struct nmport_d *d) { printf("header:\n"); printf(" nr_version: %"PRIu16"\n", d->hdr.nr_version); printf(" nr_reqtype: %"PRIu16"\n", d->hdr.nr_reqtype); printf(" nr_reserved: %"PRIu32"\n", d->hdr.nr_reserved); printf(" nr_name: %s\n", d->hdr.nr_name); printf(" nr_options: %lx\n", (unsigned long)d->hdr.nr_options); printf(" nr_body: %lx\n", (unsigned long)d->hdr.nr_body); printf("\n"); printf("register (%p):\n", (void *)d->hdr.nr_body); printf(" nr_mem_id: %"PRIu16"\n", d->reg.nr_mem_id); printf(" nr_ringid: %"PRIu16"\n", d->reg.nr_ringid); printf(" nr_mode: %lx\n", (unsigned long)d->reg.nr_mode); printf(" nr_flags: %lx\n", (unsigned long)d->reg.nr_flags); printf("\n"); if (d->hdr.nr_options) { struct nmreq_opt_extmem *e = (struct nmreq_opt_extmem *)d->hdr.nr_options; printf("opt_extmem (%p):\n", e); printf(" nro_opt.nro_next: %lx\n", (unsigned long)e->nro_opt.nro_next); printf(" nro_opt.nro_reqtype: %"PRIu32"\n", e->nro_opt.nro_reqtype); printf(" nro_usrptr: %lx\n", (unsigned long)e->nro_usrptr); printf(" nro_info.nr_memsize %"PRIu64"\n", e->nro_info.nr_memsize); } printf("\n"); printf("mem (%p):\n", d->mem); printf(" refcount: %d\n", d->mem->refcount); printf(" mem: %p\n", d->mem->mem); printf(" size: %zu\n", d->mem->size); printf("\n"); printf("rings:\n"); printf(" tx: [%d, %d]\n", d->first_tx_ring, d->last_tx_ring); printf(" rx: [%d, %d]\n", d->first_rx_ring, d->last_rx_ring); } int main(int argc, char *argv[]) { struct nmport_d *d; if (argc < 2) { fprintf(stderr, "usage: %s netmap-expr\n", argv[0]); return 1; } d = nmport_open(argv[1]); if (d != NULL) { nmreq_dump(d); nmport_close(d); } return 0; } #endif