1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $FreeBSD$ 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/ioctl.h> 35 #include <sys/mman.h> 36 #include <fcntl.h> 37 #include <inttypes.h> 38 #include <stdlib.h> 39 #include <stdio.h> 40 #include <stdarg.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <errno.h> 44 #include <net/netmap_user.h> 45 #define LIBNETMAP_NOTHREADSAFE 46 #include "libnetmap.h" 47 48 struct nmport_cleanup_d { 49 struct nmport_cleanup_d *next; 50 void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *); 51 }; 52 53 static void 54 nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c) 55 { 56 c->next = d->clist; 57 d->clist = c; 58 } 59 60 static void 61 nmport_pop_cleanup(struct nmport_d *d) 62 { 63 struct nmport_cleanup_d *top; 64 65 top = d->clist; 66 d->clist = d->clist->next; 67 (*top->cleanup)(top, d); 68 nmctx_free(d->ctx, top); 69 } 70 71 void nmport_do_cleanup(struct nmport_d *d) 72 { 73 while (d->clist != NULL) { 74 nmport_pop_cleanup(d); 75 } 76 } 77 78 static struct nmport_d * 79 nmport_new_with_ctx(struct nmctx *ctx) 80 { 81 struct nmport_d *d; 82 83 /* allocate a descriptor */ 84 d = nmctx_malloc(ctx, sizeof(*d)); 85 if (d == NULL) { 86 nmctx_ferror(ctx, "cannot allocate nmport descriptor"); 87 goto out; 88 } 89 memset(d, 0, sizeof(*d)); 90 91 nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg); 92 93 d->ctx = ctx; 94 d->fd = -1; 95 96 out: 97 return d; 98 } 99 100 struct nmport_d * 101 nmport_new(void) 102 { 103 struct nmctx *ctx = nmctx_get(); 104 return nmport_new_with_ctx(ctx); 105 } 106 107 108 void 109 nmport_delete(struct nmport_d *d) 110 { 111 nmctx_free(d->ctx, d); 112 } 113 114 void 115 nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d) 116 { 117 (void)c; 118 119 if (d->extmem == NULL) 120 return; 121 122 nmreq_remove_option(&d->hdr, &d->extmem->nro_opt); 123 nmctx_free(d->ctx, d->extmem); 124 d->extmem = NULL; 125 } 126 127 128 int 129 nmport_extmem(struct nmport_d *d, void *base, size_t size) 130 { 131 struct nmctx *ctx = d->ctx; 132 struct nmport_cleanup_d *clnup = NULL; 133 134 if (d->register_done) { 135 nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name); 136 errno = EINVAL; 137 return -1; 138 } 139 140 if (d->extmem != NULL) { 141 nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name); 142 errno = EINVAL; 143 return -1; 144 } 145 146 clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup)); 147 if (clnup == NULL) { 148 nmctx_ferror(ctx, "failed to allocate cleanup descriptor"); 149 errno = ENOMEM; 150 return -1; 151 } 152 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); 157 errno = ENOMEM; 158 return -1; 159 } 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); 165 166 clnup->cleanup = nmport_extmem_cleanup; 167 nmport_push_cleanup(d, clnup); 168 169 return 0; 170 } 171 172 struct nmport_extmem_from_file_cleanup_d { 173 struct nmport_cleanup_d up; 174 void *p; 175 size_t size; 176 }; 177 178 void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c, 179 struct nmport_d *d) 180 { 181 (void)d; 182 struct nmport_extmem_from_file_cleanup_d *cc = 183 (struct nmport_extmem_from_file_cleanup_d *)c; 184 185 munmap(cc->p, cc->size); 186 } 187 188 int 189 nmport_extmem_from_file(struct nmport_d *d, const char *fname) 190 { 191 struct nmctx *ctx = d->ctx; 192 int fd = -1; 193 off_t mapsize; 194 void *p; 195 struct nmport_extmem_from_file_cleanup_d *clnup = NULL; 196 197 clnup = nmctx_malloc(ctx, sizeof(*clnup)); 198 if (clnup == NULL) { 199 nmctx_ferror(ctx, "cannot allocate cleanup descriptor"); 200 errno = ENOMEM; 201 goto fail; 202 } 203 204 fd = open(fname, O_RDWR); 205 if (fd < 0) { 206 nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno)); 207 goto fail; 208 } 209 mapsize = lseek(fd, 0, SEEK_END); 210 if (mapsize < 0) { 211 nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno)); 212 goto fail; 213 } 214 p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 215 if (p == MAP_FAILED) { 216 nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno)); 217 goto fail; 218 } 219 close(fd); 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->p != MAP_FAILED) 236 nmport_pop_cleanup(d); 237 else 238 nmctx_free(ctx, clnup); 239 } 240 return -1; 241 } 242 243 struct nmreq_pools_info* 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 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 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 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) 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 * 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 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 * 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 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