]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libnetmap/nmport.c
unbound: Vendor import 1.13.2
[FreeBSD/FreeBSD.git] / lib / libnetmap / nmport.c
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 }