]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libnetmap/nmport.c
lib: add libnetmap
[FreeBSD/FreeBSD.git] / lib / libnetmap / nmport.c
1 /* $FreeBSD$ */
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <sys/ioctl.h>
5 #include <sys/mman.h>
6 #include <fcntl.h>
7 #include <inttypes.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <net/netmap_user.h>
15 #define LIBNETMAP_NOTHREADSAFE
16 #include "libnetmap.h"
17
18 struct nmport_cleanup_d {
19         struct nmport_cleanup_d *next;
20         void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *);
21 };
22
23 static void
24 nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c)
25 {
26         c->next = d->clist;
27         d->clist = c;
28 }
29
30 static void
31 nmport_pop_cleanup(struct nmport_d *d)
32 {
33         struct nmport_cleanup_d *top;
34
35         top = d->clist;
36         d->clist = d->clist->next;
37         (*top->cleanup)(top, d);
38         nmctx_free(d->ctx, top);
39 }
40
41 void nmport_do_cleanup(struct nmport_d *d)
42 {
43         while (d->clist != NULL) {
44                 nmport_pop_cleanup(d);
45         }
46 }
47
48 static struct nmport_d *
49 nmport_new_with_ctx(struct nmctx *ctx)
50 {
51         struct nmport_d *d;
52
53         /* allocate a descriptor */
54         d = nmctx_malloc(ctx, sizeof(*d));
55         if (d == NULL) {
56                 nmctx_ferror(ctx, "cannot allocate nmport descriptor");
57                 goto out;
58         }
59         memset(d, 0, sizeof(*d));
60
61         nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg);
62
63         d->ctx = ctx;
64         d->fd = -1;
65
66 out:
67         return d;
68 }
69
70 struct nmport_d *
71 nmport_new(void)
72 {
73         struct nmctx *ctx = nmctx_get();
74         return nmport_new_with_ctx(ctx);
75 }
76
77
78 void
79 nmport_delete(struct nmport_d *d)
80 {
81         nmctx_free(d->ctx, d);
82 }
83
84 void
85 nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d)
86 {
87         (void)c;
88
89         if (d->extmem == NULL)
90                 return;
91
92         nmreq_remove_option(&d->hdr, &d->extmem->nro_opt);
93         nmctx_free(d->ctx, d->extmem);
94         d->extmem = NULL;
95 }
96
97
98 int
99 nmport_extmem(struct nmport_d *d, void *base, size_t size)
100 {
101         struct nmctx *ctx = d->ctx;
102         struct nmport_cleanup_d *clnup = NULL;
103
104         if (d->register_done) {
105                 nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name);
106                 errno = EINVAL;
107                 return -1;
108         }
109
110         if (d->extmem != NULL) {
111                 nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name);
112                 errno = EINVAL;
113                 return -1;
114         }
115
116         clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup));
117         if (clnup == NULL) {
118                 nmctx_ferror(ctx, "failed to allocate cleanup descriptor");
119                 errno = ENOMEM;
120                 return -1;
121         }
122
123         d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem));
124         if (d->extmem == NULL) {
125                 nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name);
126                 nmctx_free(ctx, clnup);
127                 errno = ENOMEM;
128                 return -1;
129         }
130         memset(d->extmem, 0, sizeof(*d->extmem));
131         d->extmem->nro_usrptr = (uintptr_t)base;
132         d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
133         d->extmem->nro_info.nr_memsize = size;
134         nmreq_push_option(&d->hdr, &d->extmem->nro_opt);
135
136         clnup->cleanup = nmport_extmem_cleanup;
137         nmport_push_cleanup(d, clnup);
138
139         return 0;
140 }
141
142 struct nmport_extmem_from_file_cleanup_d {
143         struct nmport_cleanup_d up;
144         void *p;
145         size_t size;
146 };
147
148 void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
149                 struct nmport_d *d)
150 {
151         struct nmport_extmem_from_file_cleanup_d *cc =
152                 (struct nmport_extmem_from_file_cleanup_d *)c;
153
154         munmap(cc->p, cc->size);
155 }
156
157 int
158 nmport_extmem_from_file(struct nmport_d *d, const char *fname)
159 {
160         struct nmctx *ctx = d->ctx;
161         int fd = -1;
162         off_t mapsize;
163         void *p;
164         struct nmport_extmem_from_file_cleanup_d *clnup = NULL;
165
166         clnup = nmctx_malloc(ctx, sizeof(*clnup));
167         if (clnup == NULL) {
168                 nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
169                 errno = ENOMEM;
170                 goto fail;
171         }
172
173         fd = open(fname, O_RDWR);
174         if (fd < 0) {
175                 nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno));
176                 goto fail;
177         }
178         mapsize = lseek(fd, 0, SEEK_END);
179         if (mapsize < 0) {
180                 nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno));
181                 goto fail;
182         }
183         p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
184         if (p == MAP_FAILED) {
185                 nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno));
186                 goto fail;
187         }
188         close(fd);
189
190         clnup->p = p;
191         clnup->size = mapsize;
192         clnup->up.cleanup = nmport_extmem_from_file_cleanup;
193         nmport_push_cleanup(d, &clnup->up);
194
195         if (nmport_extmem(d, p, mapsize) < 0)
196                 goto fail;
197
198         return 0;
199
200 fail:
201         if (fd >= 0)
202                 close(fd);
203         if (clnup != NULL) {
204                 if (clnup->p != MAP_FAILED)
205                         nmport_pop_cleanup(d);
206                 else
207                         nmctx_free(ctx, clnup);
208         }
209         return -1;
210 }
211
212 struct nmreq_pools_info*
213 nmport_extmem_getinfo(struct nmport_d *d)
214 {
215         if (d->extmem == NULL)
216                 return NULL;
217         return &d->extmem->nro_info;
218 }
219
220 /* head of the list of options */
221 static struct nmreq_opt_parser *nmport_opt_parsers;
222
223 #define NPOPT_PARSER(o)         nmport_opt_##o##_parser
224 #define NPOPT_DESC(o)           nmport_opt_##o##_desc
225 #define NPOPT_NRKEYS(o)         (NPOPT_DESC(o).nr_keys)
226 #define NPOPT_DECL(o, f)                                                \
227 static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *);                   \
228 static struct nmreq_opt_parser NPOPT_DESC(o) = {                        \
229         .prefix = #o,                                                   \
230         .parse = NPOPT_PARSER(o),                                       \
231         .flags = (f),                                                   \
232         .default_key = -1,                                              \
233         .nr_keys = 0,                                                   \
234         .next = NULL,                                                   \
235 };                                                                      \
236 static void __attribute__((constructor))                                \
237 nmport_opt_##o##_ctor(void)                                             \
238 {                                                                       \
239         NPOPT_DESC(o).next = nmport_opt_parsers;                        \
240         nmport_opt_parsers = &NPOPT_DESC(o);                            \
241 }
242 struct nmport_key_desc {
243         struct nmreq_opt_parser *option;
244         const char *key;
245         unsigned int flags;
246         int id;
247 };
248 static void
249 nmport_opt_key_ctor(struct nmport_key_desc *k)
250 {
251         struct nmreq_opt_parser *o = k->option;
252         struct nmreq_opt_key *ok;
253
254         k->id = o->nr_keys;
255         ok = &o->keys[k->id];
256         ok->key = k->key;
257         ok->id = k->id;
258         ok->flags = k->flags;
259         o->nr_keys++;
260         if (ok->flags & NMREQ_OPTK_DEFAULT)
261                 o->default_key = ok->id;
262 }
263 #define NPKEY_DESC(o, k)        nmport_opt_##o##_key_##k##_desc
264 #define NPKEY_ID(o, k)          (NPKEY_DESC(o, k).id)
265 #define NPKEY_DECL(o, k, f)                                             \
266 static struct nmport_key_desc NPKEY_DESC(o, k) = {                      \
267         .option = &NPOPT_DESC(o),                                       \
268         .key = #k,                                                      \
269         .flags = (f),                                                   \
270         .id = -1,                                                       \
271 };                                                                      \
272 static void __attribute__((constructor))                                \
273 nmport_opt_##o##_key_##k##_ctor(void)                                   \
274 {                                                                       \
275         nmport_opt_key_ctor(&NPKEY_DESC(o, k));                         \
276 }
277 #define nmport_key(p, o, k)     ((p)->keys[NPKEY_ID(o, k)])
278 #define nmport_defkey(p, o)     ((p)->keys[NPOPT_DESC(o).default_key])
279
280 NPOPT_DECL(share, 0)
281         NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
282 NPOPT_DECL(extmem, 0)
283         NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
284         NPKEY_DECL(extmem, if_num, 0)
285         NPKEY_DECL(extmem, if_size, 0)
286         NPKEY_DECL(extmem, ring_num, 0)
287         NPKEY_DECL(extmem, ring_size, 0)
288         NPKEY_DECL(extmem, buf_num, 0)
289         NPKEY_DECL(extmem, buf_size, 0)
290 NPOPT_DECL(conf, 0)
291         NPKEY_DECL(conf, rings, 0)
292         NPKEY_DECL(conf, host_rings, 0)
293         NPKEY_DECL(conf, slots, 0)
294         NPKEY_DECL(conf, tx_rings, 0)
295         NPKEY_DECL(conf, rx_rings, 0)
296         NPKEY_DECL(conf, host_tx_rings, 0)
297         NPKEY_DECL(conf, host_rx_rings, 0)
298         NPKEY_DECL(conf, tx_slots, 0)
299         NPKEY_DECL(conf, rx_slots, 0)
300
301
302 static int
303 NPOPT_PARSER(share)(struct nmreq_parse_ctx *p)
304 {
305         struct nmctx *ctx = p->ctx;
306         struct nmport_d *d = p->token;
307         int32_t mem_id;
308         const char *v = nmport_defkey(p, share);
309
310         mem_id = nmreq_get_mem_id(&v, ctx);
311         if (mem_id < 0)
312                 return -1;
313         if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) {
314                 nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"",
315                                 mem_id, d->reg.nr_mem_id);
316                 errno = EINVAL;
317                 return -1;
318         }
319         d->reg.nr_mem_id = mem_id;
320         return 0;
321 }
322
323 static int
324 NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p)
325 {
326         struct nmport_d *d;
327         struct nmreq_pools_info *pi;
328         int i;
329
330         d = p->token;
331
332         if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0)
333                 return -1;
334
335         pi = &d->extmem->nro_info;
336
337         for  (i = 0; i < NPOPT_NRKEYS(extmem); i++) {
338                 const char *k = p->keys[i];
339                 uint32_t v;
340
341                 if (k == NULL)
342                         continue;
343
344                 v = atoi(k);
345                 if (i == NPKEY_ID(extmem, if_num)) {
346                         pi->nr_if_pool_objtotal = v;
347                 } else if (i == NPKEY_ID(extmem, if_size)) {
348                         pi->nr_if_pool_objsize = v;
349                 } else if (i == NPKEY_ID(extmem, ring_num)) {
350                         pi->nr_ring_pool_objtotal = v;
351                 } else if (i == NPKEY_ID(extmem, ring_size)) {
352                         pi->nr_ring_pool_objsize = v;
353                 } else if (i == NPKEY_ID(extmem, buf_num)) {
354                         pi->nr_buf_pool_objtotal = v;
355                 } else if (i == NPKEY_ID(extmem, buf_size)) {
356                         pi->nr_buf_pool_objsize = v;
357                 }
358         }
359         return 0;
360 }
361
362 static int
363 NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
364 {
365         struct nmport_d *d;
366
367         d = p->token;
368
369         if (nmport_key(p, conf, rings) != NULL) {
370                 uint16_t nr_rings = atoi(nmport_key(p, conf, rings));
371                 d->reg.nr_tx_rings = nr_rings;
372                 d->reg.nr_rx_rings = nr_rings;
373         }
374         if (nmport_key(p, conf, host_rings) != NULL) {
375                 uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings));
376                 d->reg.nr_host_tx_rings = nr_rings;
377                 d->reg.nr_host_rx_rings = nr_rings;
378         }
379         if (nmport_key(p, conf, slots) != NULL) {
380                 uint32_t nr_slots = atoi(nmport_key(p, conf, slots));
381                 d->reg.nr_tx_slots = nr_slots;
382                 d->reg.nr_rx_slots = nr_slots;
383         }
384         if (nmport_key(p, conf, tx_rings) != NULL) {
385                 d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings));
386         }
387         if (nmport_key(p, conf, rx_rings) != NULL) {
388                 d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings));
389         }
390         if (nmport_key(p, conf, host_tx_rings) != NULL) {
391                 d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings));
392         }
393         if (nmport_key(p, conf, host_rx_rings) != NULL) {
394                 d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings));
395         }
396         if (nmport_key(p, conf, tx_slots) != NULL) {
397                 d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots));
398         }
399         if (nmport_key(p, conf, rx_slots) != NULL) {
400                 d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots));
401         }
402         return 0;
403 }
404
405 void
406 nmport_disable_option(const char *opt)
407 {
408         struct nmreq_opt_parser *p;
409
410         for (p = nmport_opt_parsers; p != NULL; p = p->next) {
411                 if (!strcmp(p->prefix, opt)) {
412                         p->flags |= NMREQ_OPTF_DISABLED;
413                 }
414         }
415 }
416
417 int
418 nmport_enable_option(const char *opt)
419 {
420         struct nmreq_opt_parser *p;
421
422         for (p = nmport_opt_parsers; p != NULL; p = p->next) {
423                 if (!strcmp(p->prefix, opt)) {
424                         p->flags &= ~NMREQ_OPTF_DISABLED;
425                         return 0;
426                 }
427         }
428         errno = EOPNOTSUPP;
429         return -1;
430 }
431
432
433 int
434 nmport_parse(struct nmport_d *d, const char *ifname)
435 {
436         const char *scan = ifname;
437
438         if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) {
439                 goto err;
440         }
441
442         /* parse the register request */
443         if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) {
444                 goto err;
445         }
446
447         /* parse the options, if any */
448         if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) {
449                 goto err;
450         }
451         return 0;
452
453 err:
454         nmport_undo_parse(d);
455         return -1;
456 }
457
458 void
459 nmport_undo_parse(struct nmport_d *d)
460 {
461         nmport_do_cleanup(d);
462         memset(&d->reg, 0, sizeof(d->reg));
463         memset(&d->hdr, 0, sizeof(d->hdr));
464 }
465
466 struct nmport_d *
467 nmport_prepare(const char *ifname)
468 {
469         struct nmport_d *d;
470
471         /* allocate a descriptor */
472         d = nmport_new();
473         if (d == NULL)
474                 goto err;
475
476         /* parse the header */
477         if (nmport_parse(d, ifname) < 0)
478                 goto err;
479
480         return d;
481
482 err:
483         nmport_undo_prepare(d);
484         return NULL;
485 }
486
487 void
488 nmport_undo_prepare(struct nmport_d *d)
489 {
490         if (d == NULL)
491                 return;
492         nmport_undo_parse(d);
493         nmport_delete(d);
494 }
495
496 int
497 nmport_register(struct nmport_d *d)
498 {
499         struct nmctx *ctx = d->ctx;
500
501         if (d->register_done) {
502                 errno = EINVAL;
503                 nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name);
504                 return -1;
505         }
506
507         d->fd = open("/dev/netmap", O_RDWR);
508         if (d->fd < 0) {
509                 nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno));
510                 goto err;
511         }
512
513         if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) {
514                 struct nmreq_option *o;
515                 int option_errors = 0;
516
517                 nmreq_foreach_option(&d->hdr, o) {
518                         if (o->nro_status) {
519                                 nmctx_ferror(ctx, "%s: option %s: %s",
520                                                 d->hdr.nr_name,
521                                                 nmreq_option_name(o->nro_reqtype),
522                                                 strerror(o->nro_status));
523                                 option_errors++;
524                         }
525
526                 }
527                 if (!option_errors)
528                         nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno));
529                 goto err;
530         }
531
532         d->register_done = 1;
533
534         return 0;
535
536 err:
537         nmport_undo_register(d);
538         return -1;
539 }
540
541 void
542 nmport_undo_register(struct nmport_d *d)
543 {
544         if (d->fd >= 0)
545                 close(d->fd);
546         d->fd = -1;
547         d->register_done = 0;
548 }
549
550 /* lookup the mem_id in the mem-list: do a new mmap() if
551  * not found, reuse existing otherwise
552  */
553 int
554 nmport_mmap(struct nmport_d *d)
555 {
556         struct nmctx *ctx = d->ctx;
557         struct nmem_d *m = NULL;
558         u_int num_tx, num_rx;
559         int i;
560
561         if (d->mmap_done) {
562                 errno = EINVAL;
563                 nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name);
564                 return -1;
565         }
566
567         if (!d->register_done) {
568                 errno = EINVAL;
569                 nmctx_ferror(ctx, "cannot map unregistered port");
570                 return -1;
571         }
572
573         nmctx_lock(ctx);
574
575         for (m = ctx->mem_descs; m != NULL; m = m->next)
576                 if (m->mem_id == d->reg.nr_mem_id)
577                         break;
578
579         if (m == NULL) {
580                 m = nmctx_malloc(ctx, sizeof(*m));
581                 if (m == NULL) {
582                         nmctx_ferror(ctx, "cannot allocate memory descriptor");
583                         goto err;
584                 }
585                 memset(m, 0, sizeof(*m));
586                 if (d->extmem != NULL) {
587                         m->mem = (void *)d->extmem->nro_usrptr;
588                         m->size = d->extmem->nro_info.nr_memsize;
589                         m->is_extmem = 1;
590                 } else {
591                         m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE,
592                                         MAP_SHARED, d->fd, 0);
593                         if (m->mem == MAP_FAILED) {
594                                 nmctx_ferror(ctx, "mmap: %s", strerror(errno));
595                                 goto err;
596                         }
597                         m->size = d->reg.nr_memsize;
598                 }
599                 m->mem_id = d->reg.nr_mem_id;
600                 m->next = ctx->mem_descs;
601                 if (ctx->mem_descs != NULL)
602                         ctx->mem_descs->prev = m;
603                 ctx->mem_descs = m;
604         }
605         m->refcount++;
606
607         nmctx_unlock(ctx);
608
609         d->mem = m;
610
611         d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset);
612
613         num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
614         for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
615                 ;
616         d->first_tx_ring = i;
617         for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
618                 ;
619         d->last_tx_ring = i - 1;
620
621         num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
622         for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
623                 ;
624         d->first_rx_ring = i;
625         for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
626                 ;
627         d->last_rx_ring = i - 1;
628
629         d->mmap_done = 1;
630
631         return 0;
632
633 err:
634         nmctx_unlock(ctx);
635         nmport_undo_mmap(d);
636         return -1;
637 }
638
639 void
640 nmport_undo_mmap(struct nmport_d *d)
641 {
642         struct nmem_d *m;
643         struct nmctx *ctx = d->ctx;
644
645         m = d->mem;
646         if (m == NULL)
647                 return;
648         nmctx_lock(ctx);
649         m->refcount--;
650         if (m->refcount <= 0) {
651                 if (!m->is_extmem && m->mem != MAP_FAILED)
652                         munmap(m->mem, m->size);
653                 /* extract from the list and free */
654                 if (m->next != NULL)
655                         m->next->prev = m->prev;
656                 if (m->prev != NULL)
657                         m->prev->next = m->next;
658                 else
659                         ctx->mem_descs = m->next;
660                 nmctx_free(ctx, m);
661                 d->mem = NULL;
662         }
663         nmctx_unlock(ctx);
664         d->mmap_done = 0;
665         d->mem = NULL;
666         d->nifp = NULL;
667         d->first_tx_ring = 0;
668         d->last_tx_ring = 0;
669         d->first_rx_ring = 0;
670         d->last_rx_ring = 0;
671         d->cur_tx_ring = 0;
672         d->cur_rx_ring = 0;
673 }
674
675 int
676 nmport_open_desc(struct nmport_d *d)
677 {
678         if (nmport_register(d) < 0)
679                 goto err;
680
681         if (nmport_mmap(d) < 0)
682                 goto err;
683
684         return 0;
685 err:
686         nmport_undo_open_desc(d);
687         return -1;
688 }
689
690 void
691 nmport_undo_open_desc(struct nmport_d *d)
692 {
693         nmport_undo_mmap(d);
694         nmport_undo_register(d);
695 }
696
697
698 struct nmport_d *
699 nmport_open(const char *ifname)
700 {
701         struct nmport_d *d;
702
703         /* prepare the descriptor */
704         d = nmport_prepare(ifname);
705         if (d == NULL)
706                 goto err;
707
708         /* open netmap and register */
709         if (nmport_open_desc(d) < 0)
710                 goto err;
711
712         return d;
713
714 err:
715         nmport_close(d);
716         return NULL;
717 }
718
719 void
720 nmport_close(struct nmport_d *d)
721 {
722         if (d == NULL)
723                 return;
724         nmport_undo_open_desc(d);
725         nmport_undo_prepare(d);
726 }
727
728 struct nmport_d *
729 nmport_clone(struct nmport_d *d)
730 {
731         struct nmport_d *c;
732         struct nmctx *ctx;
733
734         ctx = d->ctx;
735
736         if (d->extmem != NULL && !d->register_done) {
737                 errno = EINVAL;
738                 nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem");
739                 return NULL;
740         }
741
742         c = nmport_new_with_ctx(ctx);
743         if (c == NULL)
744                 return NULL;
745         /* copy the output of parse */
746         c->hdr = d->hdr;
747         /* redirect the pointer to the body */
748         c->hdr.nr_body = (uintptr_t)&c->reg;
749         /* options are not cloned */
750         c->hdr.nr_options = 0;
751         c->reg = d->reg; /* this also copies the mem_id */
752         /* put the new port in an un-registered, unmapped state */
753         c->fd = -1;
754         c->nifp = NULL;
755         c->register_done = 0;
756         c->mem = NULL;
757         c->extmem = NULL;
758         c->mmap_done = 0;
759         c->first_tx_ring = 0;
760         c->last_tx_ring = 0;
761         c->first_rx_ring = 0;
762         c->last_rx_ring = 0;
763         c->cur_tx_ring = 0;
764         c->cur_rx_ring = 0;
765
766         return c;
767 }
768
769 int
770 nmport_inject(struct nmport_d *d, const void *buf, size_t size)
771 {
772         u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
773                 ri = d->cur_tx_ring;
774
775         for (c = 0; c < n ; c++, ri++) {
776                 /* compute current ring to use */
777                 struct netmap_ring *ring;
778                 uint32_t i, j, idx;
779                 size_t rem;
780
781                 if (ri > d->last_tx_ring)
782                         ri = d->first_tx_ring;
783                 ring = NETMAP_TXRING(d->nifp, ri);
784                 rem = size;
785                 j = ring->cur;
786                 while (rem > ring->nr_buf_size && j != ring->tail) {
787                         rem -= ring->nr_buf_size;
788                         j = nm_ring_next(ring, j);
789                 }
790                 if (j == ring->tail && rem > 0)
791                         continue;
792                 i = ring->cur;
793                 while (i != j) {
794                         idx = ring->slot[i].buf_idx;
795                         ring->slot[i].len = ring->nr_buf_size;
796                         ring->slot[i].flags = NS_MOREFRAG;
797                         nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
798                         i = nm_ring_next(ring, i);
799                         buf = (char *)buf + ring->nr_buf_size;
800                 }
801                 idx = ring->slot[i].buf_idx;
802                 ring->slot[i].len = rem;
803                 ring->slot[i].flags = 0;
804                 nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
805                 ring->head = ring->cur = nm_ring_next(ring, i);
806                 d->cur_tx_ring = ri;
807                 return size;
808         }
809         return 0; /* fail */
810 }