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