]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/netmap/netmap_pt.c
netmap: align if_ptnet to the changes introduced by r347233
[FreeBSD/FreeBSD.git] / sys / dev / netmap / netmap_pt.c
1 /*
2  * Copyright (C) 2015 Stefano Garzarella
3  * Copyright (C) 2016 Vincenzo Maffione
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *   2. Redistributions in binary form must reproduce the above copyright
12  *      notice, this list of conditions and the following disclaimer in the
13  *      documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 /*
31  * common headers
32  */
33 #if defined(__FreeBSD__)
34 #include <sys/cdefs.h>
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/types.h>
38 #include <sys/selinfo.h>
39 #include <sys/socket.h>
40 #include <net/if.h>
41 #include <net/if_var.h>
42 #include <machine/bus.h>
43
44 //#define usleep_range(_1, _2)
45 #define usleep_range(_1, _2) \
46         pause_sbt("ptnetmap-sleep", SBT_1US * _1, SBT_1US * 1, C_ABSOLUTE)
47
48 #elif defined(linux)
49 #include <bsd_glue.h>
50 #endif
51
52 #include <net/netmap.h>
53 #include <dev/netmap/netmap_kern.h>
54 #include <net/netmap_virt.h>
55 #include <dev/netmap/netmap_mem2.h>
56
57 #ifdef WITH_PTNETMAP_HOST
58
59 /* RX cycle without receive any packets */
60 #define PTN_RX_DRY_CYCLES_MAX   10
61
62 /* Limit Batch TX to half ring.
63  * Currently disabled, since it does not manage NS_MOREFRAG, which
64  * results in random drops in the VALE txsync. */
65 //#define PTN_TX_BATCH_LIM(_n)  ((_n >> 1))
66
67 //#define BUSY_WAIT
68
69 #define NETMAP_PT_DEBUG  /* Enables communication debugging. */
70 #ifdef NETMAP_PT_DEBUG
71 #define DBG(x) x
72 #else
73 #define DBG(x)
74 #endif
75
76
77 #undef RATE
78 //#define RATE  /* Enables communication statistics. */
79 #ifdef RATE
80 #define IFRATE(x) x
81 struct rate_batch_stats {
82     unsigned long sync;
83     unsigned long sync_dry;
84     unsigned long pkt;
85 };
86
87 struct rate_stats {
88     unsigned long gtxk;     /* Guest --> Host Tx kicks. */
89     unsigned long grxk;     /* Guest --> Host Rx kicks. */
90     unsigned long htxk;     /* Host --> Guest Tx kicks. */
91     unsigned long hrxk;     /* Host --> Guest Rx Kicks. */
92     unsigned long btxwu;    /* Backend Tx wake-up. */
93     unsigned long brxwu;    /* Backend Rx wake-up. */
94     struct rate_batch_stats txbs;
95     struct rate_batch_stats rxbs;
96 };
97
98 struct rate_context {
99     struct timer_list timer;
100     struct rate_stats new;
101     struct rate_stats old;
102 };
103
104 #define RATE_PERIOD  2
105 static void
106 rate_callback(unsigned long arg)
107 {
108     struct rate_context * ctx = (struct rate_context *)arg;
109     struct rate_stats cur = ctx->new;
110     struct rate_batch_stats *txbs = &cur.txbs;
111     struct rate_batch_stats *rxbs = &cur.rxbs;
112     struct rate_batch_stats *txbs_old = &ctx->old.txbs;
113     struct rate_batch_stats *rxbs_old = &ctx->old.rxbs;
114     uint64_t tx_batch, rx_batch;
115     unsigned long txpkts, rxpkts;
116     unsigned long gtxk, grxk;
117     int r;
118
119     txpkts = txbs->pkt - txbs_old->pkt;
120     rxpkts = rxbs->pkt - rxbs_old->pkt;
121
122     tx_batch = ((txbs->sync - txbs_old->sync) > 0) ?
123                txpkts / (txbs->sync - txbs_old->sync): 0;
124     rx_batch = ((rxbs->sync - rxbs_old->sync) > 0) ?
125                rxpkts / (rxbs->sync - rxbs_old->sync): 0;
126
127     /* Fix-up gtxk and grxk estimates. */
128     gtxk = (cur.gtxk - ctx->old.gtxk) - (cur.btxwu - ctx->old.btxwu);
129     grxk = (cur.grxk - ctx->old.grxk) - (cur.brxwu - ctx->old.brxwu);
130
131     printk("txpkts  = %lu Hz\n", txpkts/RATE_PERIOD);
132     printk("gtxk    = %lu Hz\n", gtxk/RATE_PERIOD);
133     printk("htxk    = %lu Hz\n", (cur.htxk - ctx->old.htxk)/RATE_PERIOD);
134     printk("btxw    = %lu Hz\n", (cur.btxwu - ctx->old.btxwu)/RATE_PERIOD);
135     printk("rxpkts  = %lu Hz\n", rxpkts/RATE_PERIOD);
136     printk("grxk    = %lu Hz\n", grxk/RATE_PERIOD);
137     printk("hrxk    = %lu Hz\n", (cur.hrxk - ctx->old.hrxk)/RATE_PERIOD);
138     printk("brxw    = %lu Hz\n", (cur.brxwu - ctx->old.brxwu)/RATE_PERIOD);
139     printk("txbatch = %llu avg\n", tx_batch);
140     printk("rxbatch = %llu avg\n", rx_batch);
141     printk("\n");
142
143     ctx->old = cur;
144     r = mod_timer(&ctx->timer, jiffies +
145             msecs_to_jiffies(RATE_PERIOD * 1000));
146     if (unlikely(r))
147         D("[ptnetmap] Error: mod_timer()\n");
148 }
149
150 static void
151 rate_batch_stats_update(struct rate_batch_stats *bf, uint32_t pre_tail,
152                         uint32_t act_tail, uint32_t num_slots)
153 {
154     int n = (int)act_tail - pre_tail;
155
156     if (n) {
157         if (n < 0)
158             n += num_slots;
159
160         bf->sync++;
161         bf->pkt += n;
162     } else {
163         bf->sync_dry++;
164     }
165 }
166
167 #else /* !RATE */
168 #define IFRATE(x)
169 #endif /* RATE */
170
171 struct ptnetmap_state {
172         /* Kthreads. */
173         struct nm_kctx **kctxs;
174
175         /* Shared memory with the guest (TX/RX) */
176         struct ptnet_csb_gh __user *csb_gh;
177         struct ptnet_csb_hg __user *csb_hg;
178
179         bool stopped;
180
181         /* Netmap adapter wrapping the backend. */
182         struct netmap_pt_host_adapter *pth_na;
183
184         IFRATE(struct rate_context rate_ctx;)
185 };
186
187 static inline void
188 ptnetmap_kring_dump(const char *title, const struct netmap_kring *kring)
189 {
190         D("%s - name: %s hwcur: %d hwtail: %d rhead: %d rcur: %d"
191                 " rtail: %d head: %d cur: %d tail: %d",
192                 title, kring->name, kring->nr_hwcur,
193                 kring->nr_hwtail, kring->rhead, kring->rcur, kring->rtail,
194                 kring->ring->head, kring->ring->cur, kring->ring->tail);
195 }
196
197 /*
198  * TX functions to set/get and to handle host/guest kick.
199  */
200
201
202 /* Enable or disable guest --> host kicks. */
203 static inline void
204 pthg_kick_enable(struct ptnet_csb_hg __user *pthg, uint32_t val)
205 {
206     CSB_WRITE(pthg, host_need_kick, val);
207 }
208
209 /* Are guest interrupt enabled or disabled? */
210 static inline uint32_t
211 ptgh_intr_enabled(struct ptnet_csb_gh __user *ptgh)
212 {
213     uint32_t v;
214
215     CSB_READ(ptgh, guest_need_kick, v);
216
217     return v;
218 }
219
220 /* Handle TX events: from the guest or from the backend */
221 static void
222 ptnetmap_tx_handler(void *data, int is_kthread)
223 {
224     struct netmap_kring *kring = data;
225     struct netmap_pt_host_adapter *pth_na =
226                 (struct netmap_pt_host_adapter *)kring->na->na_private;
227     struct ptnetmap_state *ptns = pth_na->ptns;
228     struct ptnet_csb_gh __user *ptgh;
229     struct ptnet_csb_hg __user *pthg;
230     struct netmap_ring shadow_ring; /* shadow copy of the netmap_ring */
231     bool more_txspace = false;
232     struct nm_kctx *kth;
233     uint32_t num_slots;
234     int batch;
235     IFRATE(uint32_t pre_tail);
236
237     if (unlikely(!ptns)) {
238         D("ERROR ptnetmap state is NULL");
239         return;
240     }
241
242     if (unlikely(ptns->stopped)) {
243         RD(1, "backend netmap is being stopped");
244         return;
245     }
246
247     if (unlikely(nm_kr_tryget(kring, 1, NULL))) {
248         D("ERROR nm_kr_tryget()");
249         return;
250     }
251
252     /* This is a guess, to be fixed in the rate callback. */
253     IFRATE(ptns->rate_ctx.new.gtxk++);
254
255     /* Get TX ptgh/pthg pointer from the CSB. */
256     ptgh = ptns->csb_gh + kring->ring_id;
257     pthg = ptns->csb_hg + kring->ring_id;
258     kth = ptns->kctxs[kring->ring_id];
259
260     num_slots = kring->nkr_num_slots;
261
262     /* Disable guest --> host notifications. */
263     pthg_kick_enable(pthg, 0);
264     /* Copy the guest kring pointers from the CSB */
265     ptnetmap_host_read_kring_csb(ptgh, &shadow_ring, num_slots);
266
267     for (;;) {
268         /* If guest moves ahead too fast, let's cut the move so
269          * that we don't exceed our batch limit. */
270         batch = shadow_ring.head - kring->nr_hwcur;
271         if (batch < 0)
272             batch += num_slots;
273
274 #ifdef PTN_TX_BATCH_LIM
275         if (batch > PTN_TX_BATCH_LIM(num_slots)) {
276             uint32_t head_lim = kring->nr_hwcur + PTN_TX_BATCH_LIM(num_slots);
277
278             if (head_lim >= num_slots)
279                 head_lim -= num_slots;
280             ND(1, "batch: %d head: %d head_lim: %d", batch, shadow_ring.head,
281                                                      head_lim);
282             shadow_ring.head = head_lim;
283             batch = PTN_TX_BATCH_LIM(num_slots);
284         }
285 #endif /* PTN_TX_BATCH_LIM */
286
287         if (nm_kr_txspace(kring) <= (num_slots >> 1)) {
288             shadow_ring.flags |= NAF_FORCE_RECLAIM;
289         }
290
291         /* Netmap prologue */
292         shadow_ring.tail = kring->rtail;
293         if (unlikely(nm_txsync_prologue(kring, &shadow_ring) >= num_slots)) {
294             /* Reinit ring and enable notifications. */
295             netmap_ring_reinit(kring);
296             pthg_kick_enable(pthg, 1);
297             break;
298         }
299
300         if (unlikely(netmap_verbose & NM_VERB_TXSYNC)) {
301             ptnetmap_kring_dump("pre txsync", kring);
302         }
303
304         IFRATE(pre_tail = kring->rtail);
305         if (unlikely(kring->nm_sync(kring, shadow_ring.flags))) {
306             /* Reenable notifications. */
307             pthg_kick_enable(pthg, 1);
308             D("ERROR txsync()");
309             break;
310         }
311
312         /*
313          * Finalize
314          * Copy host hwcur and hwtail into the CSB for the guest sync(), and
315          * do the nm_sync_finalize.
316          */
317         ptnetmap_host_write_kring_csb(pthg, kring->nr_hwcur,
318                                       kring->nr_hwtail);
319         if (kring->rtail != kring->nr_hwtail) {
320             /* Some more room available in the parent adapter. */
321             kring->rtail = kring->nr_hwtail;
322             more_txspace = true;
323         }
324
325         IFRATE(rate_batch_stats_update(&ptns->rate_ctx.new.txbs, pre_tail,
326                                        kring->rtail, num_slots));
327
328         if (unlikely(netmap_verbose & NM_VERB_TXSYNC)) {
329             ptnetmap_kring_dump("post txsync", kring);
330         }
331
332 #ifndef BUSY_WAIT
333         /* Interrupt the guest if needed. */
334         if (more_txspace && ptgh_intr_enabled(ptgh) && is_kthread) {
335             /* Disable guest kick to avoid sending unnecessary kicks */
336             nm_os_kctx_send_irq(kth);
337             IFRATE(ptns->rate_ctx.new.htxk++);
338             more_txspace = false;
339         }
340 #endif
341         /* Read CSB to see if there is more work to do. */
342         ptnetmap_host_read_kring_csb(ptgh, &shadow_ring, num_slots);
343 #ifndef BUSY_WAIT
344         if (shadow_ring.head == kring->rhead) {
345             /*
346              * No more packets to transmit. We enable notifications and
347              * go to sleep, waiting for a kick from the guest when new
348              * new slots are ready for transmission.
349              */
350             if (is_kthread) {
351                 usleep_range(1,1);
352             }
353             /* Reenable notifications. */
354             pthg_kick_enable(pthg, 1);
355             /* Doublecheck. */
356             ptnetmap_host_read_kring_csb(ptgh, &shadow_ring, num_slots);
357             if (shadow_ring.head != kring->rhead) {
358                 /* We won the race condition, there are more packets to
359                  * transmit. Disable notifications and do another cycle */
360                 pthg_kick_enable(pthg, 0);
361                 continue;
362             }
363             break;
364         }
365
366         if (nm_kr_txempty(kring)) {
367             /* No more available TX slots. We stop waiting for a notification
368              * from the backend (netmap_tx_irq). */
369             ND(1, "TX ring");
370             break;
371         }
372 #endif
373         if (unlikely(ptns->stopped)) {
374             D("backend netmap is being stopped");
375             break;
376         }
377     }
378
379     nm_kr_put(kring);
380
381     if (more_txspace && ptgh_intr_enabled(ptgh) && is_kthread) {
382         nm_os_kctx_send_irq(kth);
383         IFRATE(ptns->rate_ctx.new.htxk++);
384     }
385 }
386
387 /* Called on backend nm_notify when there is no worker thread. */
388 static void
389 ptnetmap_tx_nothread_notify(void *data)
390 {
391         struct netmap_kring *kring = data;
392         struct netmap_pt_host_adapter *pth_na =
393                 (struct netmap_pt_host_adapter *)kring->na->na_private;
394         struct ptnetmap_state *ptns = pth_na->ptns;
395
396         if (unlikely(!ptns)) {
397                 D("ERROR ptnetmap state is NULL");
398                 return;
399         }
400
401         if (unlikely(ptns->stopped)) {
402                 D("backend netmap is being stopped");
403                 return;
404         }
405
406         /* We cannot access the CSB here (to check ptgh->guest_need_kick),
407          * unless we switch address space to the one of the guest. For now
408          * we unconditionally inject an interrupt. */
409         nm_os_kctx_send_irq(ptns->kctxs[kring->ring_id]);
410         IFRATE(ptns->rate_ctx.new.htxk++);
411         ND(1, "%s interrupt", kring->name);
412 }
413
414 /*
415  * We need RX kicks from the guest when (tail == head-1), where we wait
416  * for the guest to refill.
417  */
418 #ifndef BUSY_WAIT
419 static inline int
420 ptnetmap_norxslots(struct netmap_kring *kring, uint32_t g_head)
421 {
422     return (NM_ACCESS_ONCE(kring->nr_hwtail) == nm_prev(g_head,
423                             kring->nkr_num_slots - 1));
424 }
425 #endif /* !BUSY_WAIT */
426
427 /* Handle RX events: from the guest or from the backend */
428 static void
429 ptnetmap_rx_handler(void *data, int is_kthread)
430 {
431     struct netmap_kring *kring = data;
432     struct netmap_pt_host_adapter *pth_na =
433                 (struct netmap_pt_host_adapter *)kring->na->na_private;
434     struct ptnetmap_state *ptns = pth_na->ptns;
435     struct ptnet_csb_gh __user *ptgh;
436     struct ptnet_csb_hg __user *pthg;
437     struct netmap_ring shadow_ring; /* shadow copy of the netmap_ring */
438     struct nm_kctx *kth;
439     uint32_t num_slots;
440     int dry_cycles = 0;
441     bool some_recvd = false;
442     IFRATE(uint32_t pre_tail);
443
444     if (unlikely(!ptns || !ptns->pth_na)) {
445         D("ERROR ptnetmap state %p, ptnetmap host adapter %p", ptns,
446           ptns ? ptns->pth_na : NULL);
447         return;
448     }
449
450     if (unlikely(ptns->stopped)) {
451         RD(1, "backend netmap is being stopped");
452         return;
453     }
454
455     if (unlikely(nm_kr_tryget(kring, 1, NULL))) {
456         D("ERROR nm_kr_tryget()");
457         return;
458     }
459
460     /* This is a guess, to be fixed in the rate callback. */
461     IFRATE(ptns->rate_ctx.new.grxk++);
462
463     /* Get RX ptgh and pthg pointers from the CSB. */
464     ptgh = ptns->csb_gh + (pth_na->up.num_tx_rings + kring->ring_id);
465     pthg = ptns->csb_hg + (pth_na->up.num_tx_rings + kring->ring_id);
466     kth = ptns->kctxs[pth_na->up.num_tx_rings + kring->ring_id];
467
468     num_slots = kring->nkr_num_slots;
469
470     /* Disable notifications. */
471     pthg_kick_enable(pthg, 0);
472     /* Copy the guest kring pointers from the CSB */
473     ptnetmap_host_read_kring_csb(ptgh, &shadow_ring, num_slots);
474
475     for (;;) {
476         uint32_t hwtail;
477
478         /* Netmap prologue */
479         shadow_ring.tail = kring->rtail;
480         if (unlikely(nm_rxsync_prologue(kring, &shadow_ring) >= num_slots)) {
481             /* Reinit ring and enable notifications. */
482             netmap_ring_reinit(kring);
483             pthg_kick_enable(pthg, 1);
484             break;
485         }
486
487         if (unlikely(netmap_verbose & NM_VERB_RXSYNC)) {
488             ptnetmap_kring_dump("pre rxsync", kring);
489         }
490
491         IFRATE(pre_tail = kring->rtail);
492         if (unlikely(kring->nm_sync(kring, shadow_ring.flags))) {
493             /* Reenable notifications. */
494             pthg_kick_enable(pthg, 1);
495             D("ERROR rxsync()");
496             break;
497         }
498         /*
499          * Finalize
500          * Copy host hwcur and hwtail into the CSB for the guest sync()
501          */
502         hwtail = NM_ACCESS_ONCE(kring->nr_hwtail);
503         ptnetmap_host_write_kring_csb(pthg, kring->nr_hwcur, hwtail);
504         if (kring->rtail != hwtail) {
505             kring->rtail = hwtail;
506             some_recvd = true;
507             dry_cycles = 0;
508         } else {
509             dry_cycles++;
510         }
511
512         IFRATE(rate_batch_stats_update(&ptns->rate_ctx.new.rxbs, pre_tail,
513                                        kring->rtail, num_slots));
514
515         if (unlikely(netmap_verbose & NM_VERB_RXSYNC)) {
516             ptnetmap_kring_dump("post rxsync", kring);
517         }
518
519 #ifndef BUSY_WAIT
520         /* Interrupt the guest if needed. */
521         if (some_recvd && ptgh_intr_enabled(ptgh)) {
522             /* Disable guest kick to avoid sending unnecessary kicks */
523             nm_os_kctx_send_irq(kth);
524             IFRATE(ptns->rate_ctx.new.hrxk++);
525             some_recvd = false;
526         }
527 #endif
528         /* Read CSB to see if there is more work to do. */
529         ptnetmap_host_read_kring_csb(ptgh, &shadow_ring, num_slots);
530 #ifndef BUSY_WAIT
531         if (ptnetmap_norxslots(kring, shadow_ring.head)) {
532             /*
533              * No more slots available for reception. We enable notification and
534              * go to sleep, waiting for a kick from the guest when new receive
535              * slots are available.
536              */
537             usleep_range(1,1);
538             /* Reenable notifications. */
539             pthg_kick_enable(pthg, 1);
540             /* Doublecheck. */
541             ptnetmap_host_read_kring_csb(ptgh, &shadow_ring, num_slots);
542             if (!ptnetmap_norxslots(kring, shadow_ring.head)) {
543                 /* We won the race condition, more slots are available. Disable
544                  * notifications and do another cycle. */
545                 pthg_kick_enable(pthg, 0);
546                 continue;
547             }
548             break;
549         }
550
551         hwtail = NM_ACCESS_ONCE(kring->nr_hwtail);
552         if (unlikely(hwtail == kring->rhead ||
553                      dry_cycles >= PTN_RX_DRY_CYCLES_MAX)) {
554             /* No more packets to be read from the backend. We stop and
555              * wait for a notification from the backend (netmap_rx_irq). */
556             ND(1, "nr_hwtail: %d rhead: %d dry_cycles: %d",
557                hwtail, kring->rhead, dry_cycles);
558             break;
559         }
560 #endif
561         if (unlikely(ptns->stopped)) {
562             D("backend netmap is being stopped");
563             break;
564         }
565     }
566
567     nm_kr_put(kring);
568
569     /* Interrupt the guest if needed. */
570     if (some_recvd && ptgh_intr_enabled(ptgh)) {
571         nm_os_kctx_send_irq(kth);
572         IFRATE(ptns->rate_ctx.new.hrxk++);
573     }
574 }
575
576 #ifdef NETMAP_PT_DEBUG
577 static void
578 ptnetmap_print_configuration(struct ptnetmap_cfg *cfg)
579 {
580         int k;
581
582         D("ptnetmap configuration:");
583         D("  CSB @%p@:%p, num_rings=%u, cfgtype %08x", cfg->csb_gh,
584           cfg->csb_hg, cfg->num_rings, cfg->cfgtype);
585         for (k = 0; k < cfg->num_rings; k++) {
586                 switch (cfg->cfgtype) {
587                 case PTNETMAP_CFGTYPE_QEMU: {
588                         struct ptnetmap_cfgentry_qemu *e =
589                                 (struct ptnetmap_cfgentry_qemu *)(cfg+1) + k;
590                         D("    ring #%d: ioeventfd=%lu, irqfd=%lu", k,
591                                 (unsigned long)e->ioeventfd,
592                                 (unsigned long)e->irqfd);
593                         break;
594                 }
595
596                 case PTNETMAP_CFGTYPE_BHYVE:
597                 {
598                         struct ptnetmap_cfgentry_bhyve *e =
599                                 (struct ptnetmap_cfgentry_bhyve *)(cfg+1) + k;
600                         D("    ring #%d: wchan=%lu, ioctl_fd=%lu, "
601                           "ioctl_cmd=%lu, msix_msg_data=%lu, msix_addr=%lu",
602                                 k, (unsigned long)e->wchan,
603                                 (unsigned long)e->ioctl_fd,
604                                 (unsigned long)e->ioctl_cmd,
605                                 (unsigned long)e->ioctl_data.msg_data,
606                                 (unsigned long)e->ioctl_data.addr);
607                         break;
608                 }
609                 }
610         }
611
612 }
613 #endif /* NETMAP_PT_DEBUG */
614
615 /* Copy actual state of the host ring into the CSB for the guest init */
616 static int
617 ptnetmap_kring_snapshot(struct netmap_kring *kring,
618                         struct ptnet_csb_gh __user *ptgh,
619                         struct ptnet_csb_hg __user *pthg)
620 {
621     if (CSB_WRITE(ptgh, head, kring->rhead))
622         goto err;
623     if (CSB_WRITE(ptgh, cur, kring->rcur))
624         goto err;
625
626     if (CSB_WRITE(pthg, hwcur, kring->nr_hwcur))
627         goto err;
628     if (CSB_WRITE(pthg, hwtail, NM_ACCESS_ONCE(kring->nr_hwtail)))
629         goto err;
630
631     DBG(ptnetmap_kring_dump("ptnetmap_kring_snapshot", kring);)
632
633     return 0;
634 err:
635     return EFAULT;
636 }
637
638 static struct netmap_kring *
639 ptnetmap_kring(struct netmap_pt_host_adapter *pth_na, int k)
640 {
641         if (k < pth_na->up.num_tx_rings) {
642                 return pth_na->up.tx_rings[k];
643         }
644         return pth_na->up.rx_rings[k - pth_na->up.num_tx_rings];
645 }
646
647 static int
648 ptnetmap_krings_snapshot(struct netmap_pt_host_adapter *pth_na)
649 {
650         struct ptnetmap_state *ptns = pth_na->ptns;
651         struct netmap_kring *kring;
652         unsigned int num_rings;
653         int err = 0, k;
654
655         num_rings = pth_na->up.num_tx_rings +
656                     pth_na->up.num_rx_rings;
657
658         for (k = 0; k < num_rings; k++) {
659                 kring = ptnetmap_kring(pth_na, k);
660                 err |= ptnetmap_kring_snapshot(kring, ptns->csb_gh + k,
661                                                 ptns->csb_hg + k);
662         }
663
664         return err;
665 }
666
667 /*
668  * Functions to create kernel contexts, and start/stop the workers.
669  */
670
671 static int
672 ptnetmap_create_kctxs(struct netmap_pt_host_adapter *pth_na,
673                       struct ptnetmap_cfg *cfg, int use_tx_kthreads)
674 {
675         struct ptnetmap_state *ptns = pth_na->ptns;
676         struct nm_kctx_cfg nmk_cfg;
677         unsigned int num_rings;
678         uint8_t *cfg_entries = (uint8_t *)(cfg + 1);
679         unsigned int expected_cfgtype = 0;
680         int k;
681
682 #if defined(__FreeBSD__)
683         expected_cfgtype = PTNETMAP_CFGTYPE_BHYVE;
684 #elif defined(linux)
685         expected_cfgtype = PTNETMAP_CFGTYPE_QEMU;
686 #endif
687         if (cfg->cfgtype != expected_cfgtype) {
688                 D("Unsupported cfgtype %u", cfg->cfgtype);
689                 return EINVAL;
690         }
691
692         num_rings = pth_na->up.num_tx_rings +
693                     pth_na->up.num_rx_rings;
694
695         for (k = 0; k < num_rings; k++) {
696                 nmk_cfg.attach_user = 1; /* attach kthread to user process */
697                 nmk_cfg.worker_private = ptnetmap_kring(pth_na, k);
698                 nmk_cfg.type = k;
699                 if (k < pth_na->up.num_tx_rings) {
700                         nmk_cfg.worker_fn = ptnetmap_tx_handler;
701                         nmk_cfg.use_kthread = use_tx_kthreads;
702                         nmk_cfg.notify_fn = ptnetmap_tx_nothread_notify;
703                 } else {
704                         nmk_cfg.worker_fn = ptnetmap_rx_handler;
705                         nmk_cfg.use_kthread = 1;
706                 }
707
708                 ptns->kctxs[k] = nm_os_kctx_create(&nmk_cfg,
709                                 cfg_entries + k * cfg->entry_size);
710                 if (ptns->kctxs[k] == NULL) {
711                         goto err;
712                 }
713         }
714
715         return 0;
716 err:
717         for (k = 0; k < num_rings; k++) {
718                 if (ptns->kctxs[k]) {
719                         nm_os_kctx_destroy(ptns->kctxs[k]);
720                         ptns->kctxs[k] = NULL;
721                 }
722         }
723         return EFAULT;
724 }
725
726 static int
727 ptnetmap_start_kctx_workers(struct netmap_pt_host_adapter *pth_na)
728 {
729         struct ptnetmap_state *ptns = pth_na->ptns;
730         int num_rings;
731         int error;
732         int k;
733
734         if (!ptns) {
735                 D("BUG ptns is NULL");
736                 return EFAULT;
737         }
738
739         ptns->stopped = false;
740
741         num_rings = ptns->pth_na->up.num_tx_rings +
742                     ptns->pth_na->up.num_rx_rings;
743         for (k = 0; k < num_rings; k++) {
744                 //nm_os_kctx_worker_setaff(ptns->kctxs[k], xxx);
745                 error = nm_os_kctx_worker_start(ptns->kctxs[k]);
746                 if (error) {
747                         return error;
748                 }
749         }
750
751         return 0;
752 }
753
754 static void
755 ptnetmap_stop_kctx_workers(struct netmap_pt_host_adapter *pth_na)
756 {
757         struct ptnetmap_state *ptns = pth_na->ptns;
758         int num_rings;
759         int k;
760
761         if (!ptns) {
762                 /* Nothing to do. */
763                 return;
764         }
765
766         ptns->stopped = true;
767
768         num_rings = ptns->pth_na->up.num_tx_rings +
769                     ptns->pth_na->up.num_rx_rings;
770         for (k = 0; k < num_rings; k++) {
771                 nm_os_kctx_worker_stop(ptns->kctxs[k]);
772         }
773 }
774
775 static int nm_unused_notify(struct netmap_kring *, int);
776 static int nm_pt_host_notify(struct netmap_kring *, int);
777
778 /* Create ptnetmap state and switch parent adapter to ptnetmap mode. */
779 static int
780 ptnetmap_create(struct netmap_pt_host_adapter *pth_na,
781                 struct ptnetmap_cfg *cfg)
782 {
783     int use_tx_kthreads = ptnetmap_tx_workers; /* snapshot */
784     struct ptnetmap_state *ptns;
785     unsigned int num_rings;
786     int ret, i;
787
788     /* Check if ptnetmap state is already there. */
789     if (pth_na->ptns) {
790         D("ERROR adapter %p already in ptnetmap mode", pth_na->parent);
791         return EINVAL;
792     }
793
794     num_rings = pth_na->up.num_tx_rings + pth_na->up.num_rx_rings;
795
796     if (num_rings != cfg->num_rings) {
797         D("ERROR configuration mismatch, expected %u rings, found %u",
798            num_rings, cfg->num_rings);
799         return EINVAL;
800     }
801
802     if (!use_tx_kthreads && na_is_generic(pth_na->parent)) {
803         D("ERROR ptnetmap direct transmission not supported with "
804           "passed-through emulated adapters");
805         return EOPNOTSUPP;
806     }
807
808     ptns = nm_os_malloc(sizeof(*ptns) + num_rings * sizeof(*ptns->kctxs));
809     if (!ptns) {
810         return ENOMEM;
811     }
812
813     ptns->kctxs = (struct nm_kctx **)(ptns + 1);
814     ptns->stopped = true;
815
816     /* Cross-link data structures. */
817     pth_na->ptns = ptns;
818     ptns->pth_na = pth_na;
819
820     /* Store the CSB address provided by the hypervisor. */
821     ptns->csb_gh = cfg->csb_gh;
822     ptns->csb_hg = cfg->csb_hg;
823
824     DBG(ptnetmap_print_configuration(cfg));
825
826     /* Create kernel contexts. */
827     if ((ret = ptnetmap_create_kctxs(pth_na, cfg, use_tx_kthreads))) {
828         D("ERROR ptnetmap_create_kctxs()");
829         goto err;
830     }
831     /* Copy krings state into the CSB for the guest initialization */
832     if ((ret = ptnetmap_krings_snapshot(pth_na))) {
833         D("ERROR ptnetmap_krings_snapshot()");
834         goto err;
835     }
836
837     /* Overwrite parent nm_notify krings callback, and
838      * clear NAF_BDG_MAYSLEEP if needed. */
839     pth_na->parent->na_private = pth_na;
840     pth_na->parent_nm_notify = pth_na->parent->nm_notify;
841     pth_na->parent->nm_notify = nm_unused_notify;
842     pth_na->parent_na_flags = pth_na->parent->na_flags;
843     if (!use_tx_kthreads) {
844         /* VALE port txsync is executed under spinlock on Linux, so
845          * we need to make sure the bridge cannot sleep. */
846         pth_na->parent->na_flags &= ~NAF_BDG_MAYSLEEP;
847     }
848
849     for (i = 0; i < pth_na->parent->num_rx_rings; i++) {
850         pth_na->up.rx_rings[i]->save_notify =
851                 pth_na->up.rx_rings[i]->nm_notify;
852         pth_na->up.rx_rings[i]->nm_notify = nm_pt_host_notify;
853     }
854     for (i = 0; i < pth_na->parent->num_tx_rings; i++) {
855         pth_na->up.tx_rings[i]->save_notify =
856                 pth_na->up.tx_rings[i]->nm_notify;
857         pth_na->up.tx_rings[i]->nm_notify = nm_pt_host_notify;
858     }
859
860 #ifdef RATE
861     memset(&ptns->rate_ctx, 0, sizeof(ptns->rate_ctx));
862     setup_timer(&ptns->rate_ctx.timer, &rate_callback,
863             (unsigned long)&ptns->rate_ctx);
864     if (mod_timer(&ptns->rate_ctx.timer, jiffies + msecs_to_jiffies(1500)))
865         D("[ptn] Error: mod_timer()\n");
866 #endif
867
868     DBG(D("[%s] ptnetmap configuration DONE", pth_na->up.name));
869
870     return 0;
871
872 err:
873     pth_na->ptns = NULL;
874     nm_os_free(ptns);
875     return ret;
876 }
877
878 /* Switch parent adapter back to normal mode and destroy
879  * ptnetmap state. */
880 static void
881 ptnetmap_delete(struct netmap_pt_host_adapter *pth_na)
882 {
883     struct ptnetmap_state *ptns = pth_na->ptns;
884     int num_rings;
885     int i;
886
887     if (!ptns) {
888         /* Nothing to do. */
889         return;
890     }
891
892     /* Restore parent adapter callbacks. */
893     pth_na->parent->nm_notify = pth_na->parent_nm_notify;
894     pth_na->parent->na_private = NULL;
895     pth_na->parent->na_flags = pth_na->parent_na_flags;
896
897     for (i = 0; i < pth_na->parent->num_rx_rings; i++) {
898         pth_na->up.rx_rings[i]->nm_notify =
899                 pth_na->up.rx_rings[i]->save_notify;
900         pth_na->up.rx_rings[i]->save_notify = NULL;
901     }
902     for (i = 0; i < pth_na->parent->num_tx_rings; i++) {
903         pth_na->up.tx_rings[i]->nm_notify =
904                 pth_na->up.tx_rings[i]->save_notify;
905         pth_na->up.tx_rings[i]->save_notify = NULL;
906     }
907
908     /* Destroy kernel contexts. */
909     num_rings = ptns->pth_na->up.num_tx_rings +
910                 ptns->pth_na->up.num_rx_rings;
911     for (i = 0; i < num_rings; i++) {
912         nm_os_kctx_destroy(ptns->kctxs[i]);
913         ptns->kctxs[i] = NULL;
914     }
915
916     IFRATE(del_timer(&ptns->rate_ctx.timer));
917
918     nm_os_free(ptns);
919
920     pth_na->ptns = NULL;
921
922     DBG(D("[%s] ptnetmap deleted", pth_na->up.name));
923 }
924
925 /*
926  * Called by netmap_ioctl().
927  * Operation is indicated in nr_name.
928  *
929  * Called without NMG_LOCK.
930  */
931 int
932 ptnetmap_ctl(const char *nr_name, int create, struct netmap_adapter *na)
933 {
934         struct netmap_pt_host_adapter *pth_na;
935         struct ptnetmap_cfg *cfg = NULL;
936         int error = 0;
937
938         DBG(D("name: %s", nr_name));
939
940         if (!nm_ptnetmap_host_on(na)) {
941                 D("ERROR Netmap adapter %p is not a ptnetmap host adapter",
942                         na);
943                 return ENXIO;
944         }
945         pth_na = (struct netmap_pt_host_adapter *)na;
946
947         NMG_LOCK();
948         if (create) {
949                 /* Read hypervisor configuration from userspace. */
950                 /* TODO */
951                 if (!cfg) {
952                         goto out;
953                 }
954                 /* Create ptnetmap state (kctxs, ...) and switch parent
955                  * adapter to ptnetmap mode. */
956                 error = ptnetmap_create(pth_na, cfg);
957                 nm_os_free(cfg);
958                 if (error) {
959                         goto out;
960                 }
961                 /* Start kthreads. */
962                 error = ptnetmap_start_kctx_workers(pth_na);
963                 if (error)
964                         ptnetmap_delete(pth_na);
965         } else {
966                 /* Stop kthreads. */
967                 ptnetmap_stop_kctx_workers(pth_na);
968                 /* Switch parent adapter back to normal mode and destroy
969                  * ptnetmap state (kthreads, ...). */
970                 ptnetmap_delete(pth_na);
971         }
972 out:
973         NMG_UNLOCK();
974
975         return error;
976 }
977
978 /* nm_notify callbacks for ptnetmap */
979 static int
980 nm_pt_host_notify(struct netmap_kring *kring, int flags)
981 {
982         struct netmap_adapter *na = kring->na;
983         struct netmap_pt_host_adapter *pth_na =
984                 (struct netmap_pt_host_adapter *)na->na_private;
985         struct ptnetmap_state *ptns;
986         int k;
987
988         /* First check that the passthrough port is not being destroyed. */
989         if (unlikely(!pth_na)) {
990                 return NM_IRQ_COMPLETED;
991         }
992
993         ptns = pth_na->ptns;
994         if (unlikely(!ptns || ptns->stopped)) {
995                 return NM_IRQ_COMPLETED;
996         }
997
998         k = kring->ring_id;
999
1000         /* Notify kthreads (wake up if needed) */
1001         if (kring->tx == NR_TX) {
1002                 ND(1, "TX backend irq");
1003                 IFRATE(ptns->rate_ctx.new.btxwu++);
1004         } else {
1005                 k += pth_na->up.num_tx_rings;
1006                 ND(1, "RX backend irq");
1007                 IFRATE(ptns->rate_ctx.new.brxwu++);
1008         }
1009         nm_os_kctx_worker_wakeup(ptns->kctxs[k]);
1010
1011         return NM_IRQ_COMPLETED;
1012 }
1013
1014 static int
1015 nm_unused_notify(struct netmap_kring *kring, int flags)
1016 {
1017     D("BUG this should never be called");
1018     return ENXIO;
1019 }
1020
1021 /* nm_config callback for bwrap */
1022 static int
1023 nm_pt_host_config(struct netmap_adapter *na, struct nm_config_info *info)
1024 {
1025     struct netmap_pt_host_adapter *pth_na =
1026         (struct netmap_pt_host_adapter *)na;
1027     struct netmap_adapter *parent = pth_na->parent;
1028     int error;
1029
1030     //XXX: maybe calling parent->nm_config is better
1031
1032     /* forward the request */
1033     error = netmap_update_config(parent);
1034
1035     info->num_rx_rings = na->num_rx_rings = parent->num_rx_rings;
1036     info->num_tx_rings = na->num_tx_rings = parent->num_tx_rings;
1037     info->num_tx_descs = na->num_tx_desc = parent->num_tx_desc;
1038     info->num_rx_descs = na->num_rx_desc = parent->num_rx_desc;
1039     info->rx_buf_maxsize = na->rx_buf_maxsize = parent->rx_buf_maxsize;
1040
1041     return error;
1042 }
1043
1044 /* nm_krings_create callback for ptnetmap */
1045 static int
1046 nm_pt_host_krings_create(struct netmap_adapter *na)
1047 {
1048     struct netmap_pt_host_adapter *pth_na =
1049         (struct netmap_pt_host_adapter *)na;
1050     struct netmap_adapter *parent = pth_na->parent;
1051     enum txrx t;
1052     int error;
1053
1054     DBG(D("%s", pth_na->up.name));
1055
1056     /* create the parent krings */
1057     error = parent->nm_krings_create(parent);
1058     if (error) {
1059         return error;
1060     }
1061
1062     /* A ptnetmap host adapter points the very same krings
1063      * as its parent adapter. These pointer are used in the
1064      * TX/RX worker functions. */
1065     na->tx_rings = parent->tx_rings;
1066     na->rx_rings = parent->rx_rings;
1067     na->tailroom = parent->tailroom;
1068
1069     for_rx_tx(t) {
1070         struct netmap_kring *kring;
1071
1072         /* Parent's kring_create function will initialize
1073          * its own na->si. We have to init our na->si here. */
1074         nm_os_selinfo_init(&na->si[t]);
1075
1076         /* Force the mem_rings_create() method to create the
1077          * host rings independently on what the regif asked for:
1078          * these rings are needed by the guest ptnetmap adapter
1079          * anyway. */
1080         kring = NMR(na, t)[nma_get_nrings(na, t)];
1081         kring->nr_kflags |= NKR_NEEDRING;
1082     }
1083
1084     return 0;
1085 }
1086
1087 /* nm_krings_delete callback for ptnetmap */
1088 static void
1089 nm_pt_host_krings_delete(struct netmap_adapter *na)
1090 {
1091     struct netmap_pt_host_adapter *pth_na =
1092         (struct netmap_pt_host_adapter *)na;
1093     struct netmap_adapter *parent = pth_na->parent;
1094
1095     DBG(D("%s", pth_na->up.name));
1096
1097     parent->nm_krings_delete(parent);
1098
1099     na->tx_rings = na->rx_rings = na->tailroom = NULL;
1100 }
1101
1102 /* nm_register callback */
1103 static int
1104 nm_pt_host_register(struct netmap_adapter *na, int onoff)
1105 {
1106     struct netmap_pt_host_adapter *pth_na =
1107         (struct netmap_pt_host_adapter *)na;
1108     struct netmap_adapter *parent = pth_na->parent;
1109     int error;
1110     DBG(D("%s onoff %d", pth_na->up.name, onoff));
1111
1112     if (onoff) {
1113         /* netmap_do_regif has been called on the ptnetmap na.
1114          * We need to pass the information about the
1115          * memory allocator to the parent before
1116          * putting it in netmap mode
1117          */
1118         parent->na_lut = na->na_lut;
1119     }
1120
1121     /* forward the request to the parent */
1122     error = parent->nm_register(parent, onoff);
1123     if (error)
1124         return error;
1125
1126
1127     if (onoff) {
1128         na->na_flags |= NAF_NETMAP_ON | NAF_PTNETMAP_HOST;
1129     } else {
1130         ptnetmap_delete(pth_na);
1131         na->na_flags &= ~(NAF_NETMAP_ON | NAF_PTNETMAP_HOST);
1132     }
1133
1134     return 0;
1135 }
1136
1137 /* nm_dtor callback */
1138 static void
1139 nm_pt_host_dtor(struct netmap_adapter *na)
1140 {
1141     struct netmap_pt_host_adapter *pth_na =
1142         (struct netmap_pt_host_adapter *)na;
1143     struct netmap_adapter *parent = pth_na->parent;
1144
1145     DBG(D("%s", pth_na->up.name));
1146
1147     /* The equivalent of NETMAP_PT_HOST_DELETE if the hypervisor
1148      * didn't do it. */
1149     ptnetmap_stop_kctx_workers(pth_na);
1150     ptnetmap_delete(pth_na);
1151
1152     parent->na_flags &= ~NAF_BUSY;
1153
1154     netmap_adapter_put(pth_na->parent);
1155     pth_na->parent = NULL;
1156 }
1157
1158 /* check if nmr is a request for a ptnetmap adapter that we can satisfy */
1159 int
1160 netmap_get_pt_host_na(struct nmreq_header *hdr, struct netmap_adapter **na,
1161                 struct netmap_mem_d *nmd, int create)
1162 {
1163     struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
1164     struct nmreq_register preq;
1165     struct netmap_adapter *parent; /* target adapter */
1166     struct netmap_pt_host_adapter *pth_na;
1167     struct ifnet *ifp = NULL;
1168     int error;
1169
1170     /* Check if it is a request for a ptnetmap adapter */
1171     if ((req->nr_flags & (NR_PTNETMAP_HOST)) == 0) {
1172         return 0;
1173     }
1174
1175     D("Requesting a ptnetmap host adapter");
1176
1177     pth_na = nm_os_malloc(sizeof(*pth_na));
1178     if (pth_na == NULL) {
1179         D("ERROR malloc");
1180         return ENOMEM;
1181     }
1182
1183     /* first, try to find the adapter that we want to passthrough
1184      * We use the same req, after we have turned off the ptnetmap flag.
1185      * In this way we can potentially passthrough everything netmap understands.
1186      */
1187     memcpy(&preq, req, sizeof(preq));
1188     preq.nr_flags &= ~(NR_PTNETMAP_HOST);
1189     hdr->nr_body = (uintptr_t)&preq;
1190     error = netmap_get_na(hdr, &parent, &ifp, nmd, create);
1191     hdr->nr_body = (uintptr_t)req;
1192     if (error) {
1193         D("parent lookup failed: %d", error);
1194         goto put_out_noputparent;
1195     }
1196     DBG(D("found parent: %s", parent->name));
1197
1198     /* make sure the interface is not already in use */
1199     if (NETMAP_OWNED_BY_ANY(parent)) {
1200         D("NIC %s busy, cannot ptnetmap", parent->name);
1201         error = EBUSY;
1202         goto put_out;
1203     }
1204
1205     pth_na->parent = parent;
1206
1207     /* Follow netmap_attach()-like operations for the host
1208      * ptnetmap adapter. */
1209
1210     //XXX pth_na->up.na_flags = parent->na_flags;
1211     pth_na->up.num_rx_rings = parent->num_rx_rings;
1212     pth_na->up.num_tx_rings = parent->num_tx_rings;
1213     pth_na->up.num_tx_desc = parent->num_tx_desc;
1214     pth_na->up.num_rx_desc = parent->num_rx_desc;
1215
1216     pth_na->up.nm_dtor = nm_pt_host_dtor;
1217     pth_na->up.nm_register = nm_pt_host_register;
1218
1219     /* Reuse parent's adapter txsync and rxsync methods. */
1220     pth_na->up.nm_txsync = parent->nm_txsync;
1221     pth_na->up.nm_rxsync = parent->nm_rxsync;
1222
1223     pth_na->up.nm_krings_create = nm_pt_host_krings_create;
1224     pth_na->up.nm_krings_delete = nm_pt_host_krings_delete;
1225     pth_na->up.nm_config = nm_pt_host_config;
1226
1227     /* Set the notify method only or convenience, it will never
1228      * be used, since - differently from default krings_create - we
1229      * ptnetmap krings_create callback inits kring->nm_notify
1230      * directly. */
1231     pth_na->up.nm_notify = nm_unused_notify;
1232
1233     pth_na->up.nm_mem = netmap_mem_get(parent->nm_mem);
1234
1235     pth_na->up.na_flags |= NAF_HOST_RINGS;
1236
1237     error = netmap_attach_common(&pth_na->up);
1238     if (error) {
1239         D("ERROR netmap_attach_common()");
1240         goto put_out;
1241     }
1242
1243     *na = &pth_na->up;
1244     /* set parent busy, because attached for ptnetmap */
1245     parent->na_flags |= NAF_BUSY;
1246     strncpy(pth_na->up.name, parent->name, sizeof(pth_na->up.name));
1247     strcat(pth_na->up.name, "-PTN");
1248     netmap_adapter_get(*na);
1249
1250     DBG(D("%s ptnetmap request DONE", pth_na->up.name));
1251
1252     /* drop the reference to the ifp, if any */
1253     if (ifp)
1254         if_rele(ifp);
1255
1256     return 0;
1257
1258 put_out:
1259     netmap_adapter_put(parent);
1260     if (ifp)
1261         if_rele(ifp);
1262 put_out_noputparent:
1263     nm_os_free(pth_na);
1264     return error;
1265 }
1266 #endif /* WITH_PTNETMAP_HOST */
1267
1268 #ifdef WITH_PTNETMAP_GUEST
1269 /*
1270  * Guest ptnetmap txsync()/rxsync() routines, used in ptnet device drivers.
1271  * These routines are reused across the different operating systems supported
1272  * by netmap.
1273  */
1274
1275 /*
1276  * Reconcile host and guest views of the transmit ring.
1277  *
1278  * Guest user wants to transmit packets up to the one before ring->head,
1279  * and guest kernel knows tx_ring->hwcur is the first packet unsent
1280  * by the host kernel.
1281  *
1282  * We push out as many packets as possible, and possibly
1283  * reclaim buffers from previously completed transmission.
1284  *
1285  * Notifications from the host are enabled only if the user guest would
1286  * block (no space in the ring).
1287  */
1288 bool
1289 netmap_pt_guest_txsync(struct ptnet_csb_gh *ptgh, struct ptnet_csb_hg *pthg,
1290                         struct netmap_kring *kring, int flags)
1291 {
1292         bool notify = false;
1293
1294         /* Disable notifications */
1295         ptgh->guest_need_kick = 0;
1296
1297         /*
1298          * First part: tell the host (updating the CSB) to process the new
1299          * packets.
1300          */
1301         kring->nr_hwcur = pthg->hwcur;
1302         ptnetmap_guest_write_kring_csb(ptgh, kring->rcur, kring->rhead);
1303
1304         /* Ask for a kick from a guest to the host if needed. */
1305         if (((kring->rhead != kring->nr_hwcur || nm_kr_txempty(kring))
1306                 && NM_ACCESS_ONCE(pthg->host_need_kick)) ||
1307                         (flags & NAF_FORCE_RECLAIM)) {
1308                 ptgh->sync_flags = flags;
1309                 notify = true;
1310         }
1311
1312         /*
1313          * Second part: reclaim buffers for completed transmissions.
1314          */
1315         if (nm_kr_txempty(kring) || (flags & NAF_FORCE_RECLAIM)) {
1316                 ptnetmap_guest_read_kring_csb(pthg, kring);
1317         }
1318
1319         /*
1320          * No more room in the ring for new transmissions. The user thread will
1321          * go to sleep and we need to be notified by the host when more free
1322          * space is available.
1323          */
1324         if (nm_kr_txempty(kring) && !(kring->nr_kflags & NKR_NOINTR)) {
1325                 /* Reenable notifications. */
1326                 ptgh->guest_need_kick = 1;
1327                 /* Double check */
1328                 ptnetmap_guest_read_kring_csb(pthg, kring);
1329                 /* If there is new free space, disable notifications */
1330                 if (unlikely(!nm_kr_txempty(kring))) {
1331                         ptgh->guest_need_kick = 0;
1332                 }
1333         }
1334
1335         ND(1, "%s CSB(head:%u cur:%u hwtail:%u) KRING(head:%u cur:%u tail:%u)",
1336                 kring->name, ptgh->head, ptgh->cur, pthg->hwtail,
1337                 kring->rhead, kring->rcur, kring->nr_hwtail);
1338
1339         return notify;
1340 }
1341
1342 /*
1343  * Reconcile host and guest view of the receive ring.
1344  *
1345  * Update hwcur/hwtail from host (reading from CSB).
1346  *
1347  * If guest user has released buffers up to the one before ring->head, we
1348  * also give them to the host.
1349  *
1350  * Notifications from the host are enabled only if the user guest would
1351  * block (no more completed slots in the ring).
1352  */
1353 bool
1354 netmap_pt_guest_rxsync(struct ptnet_csb_gh *ptgh, struct ptnet_csb_hg *pthg,
1355                         struct netmap_kring *kring, int flags)
1356 {
1357         bool notify = false;
1358
1359         /* Disable notifications */
1360         ptgh->guest_need_kick = 0;
1361
1362         /*
1363          * First part: import newly received packets, by updating the kring
1364          * hwtail to the hwtail known from the host (read from the CSB).
1365          * This also updates the kring hwcur.
1366          */
1367         ptnetmap_guest_read_kring_csb(pthg, kring);
1368         kring->nr_kflags &= ~NKR_PENDINTR;
1369
1370         /*
1371          * Second part: tell the host about the slots that guest user has
1372          * released, by updating cur and head in the CSB.
1373          */
1374         if (kring->rhead != kring->nr_hwcur) {
1375                 ptnetmap_guest_write_kring_csb(ptgh, kring->rcur,
1376                                                kring->rhead);
1377                 /* Ask for a kick from the guest to the host if needed. */
1378                 if (NM_ACCESS_ONCE(pthg->host_need_kick)) {
1379                         ptgh->sync_flags = flags;
1380                         notify = true;
1381                 }
1382         }
1383
1384         /*
1385          * No more completed RX slots. The user thread will go to sleep and
1386          * we need to be notified by the host when more RX slots have been
1387          * completed.
1388          */
1389         if (nm_kr_rxempty(kring) && !(kring->nr_kflags & NKR_NOINTR)) {
1390                 /* Reenable notifications. */
1391                 ptgh->guest_need_kick = 1;
1392                 /* Double check */
1393                 ptnetmap_guest_read_kring_csb(pthg, kring);
1394                 /* If there are new slots, disable notifications. */
1395                 if (!nm_kr_rxempty(kring)) {
1396                         ptgh->guest_need_kick = 0;
1397                 }
1398         }
1399
1400         ND(1, "%s CSB(head:%u cur:%u hwtail:%u) KRING(head:%u cur:%u tail:%u)",
1401                 kring->name, ptgh->head, ptgh->cur, pthg->hwtail,
1402                 kring->rhead, kring->rcur, kring->nr_hwtail);
1403
1404         return notify;
1405 }
1406
1407 /*
1408  * Callbacks for ptnet drivers: nm_krings_create, nm_krings_delete, nm_dtor.
1409  */
1410 int
1411 ptnet_nm_krings_create(struct netmap_adapter *na)
1412 {
1413         struct netmap_pt_guest_adapter *ptna =
1414                         (struct netmap_pt_guest_adapter *)na; /* Upcast. */
1415         struct netmap_adapter *na_nm = &ptna->hwup.up;
1416         struct netmap_adapter *na_dr = &ptna->dr.up;
1417         int ret;
1418
1419         if (ptna->backend_regifs) {
1420                 return 0;
1421         }
1422
1423         /* Create krings on the public netmap adapter. */
1424         ret = netmap_hw_krings_create(na_nm);
1425         if (ret) {
1426                 return ret;
1427         }
1428
1429         /* Copy krings into the netmap adapter private to the driver. */
1430         na_dr->tx_rings = na_nm->tx_rings;
1431         na_dr->rx_rings = na_nm->rx_rings;
1432
1433         return 0;
1434 }
1435
1436 void
1437 ptnet_nm_krings_delete(struct netmap_adapter *na)
1438 {
1439         struct netmap_pt_guest_adapter *ptna =
1440                         (struct netmap_pt_guest_adapter *)na; /* Upcast. */
1441         struct netmap_adapter *na_nm = &ptna->hwup.up;
1442         struct netmap_adapter *na_dr = &ptna->dr.up;
1443
1444         if (ptna->backend_regifs) {
1445                 return;
1446         }
1447
1448         na_dr->tx_rings = NULL;
1449         na_dr->rx_rings = NULL;
1450
1451         netmap_hw_krings_delete(na_nm);
1452 }
1453
1454 void
1455 ptnet_nm_dtor(struct netmap_adapter *na)
1456 {
1457         struct netmap_pt_guest_adapter *ptna =
1458                         (struct netmap_pt_guest_adapter *)na;
1459
1460         netmap_mem_put(ptna->dr.up.nm_mem);
1461         memset(&ptna->dr, 0, sizeof(ptna->dr));
1462         netmap_mem_pt_guest_ifp_del(na->nm_mem, na->ifp);
1463 }
1464
1465 int
1466 netmap_pt_guest_attach(struct netmap_adapter *arg,
1467                        unsigned int nifp_offset, unsigned int memid)
1468 {
1469         struct netmap_pt_guest_adapter *ptna;
1470         struct ifnet *ifp = arg ? arg->ifp : NULL;
1471         int error;
1472
1473         /* get allocator */
1474         arg->nm_mem = netmap_mem_pt_guest_new(ifp, nifp_offset, memid);
1475         if (arg->nm_mem == NULL)
1476                 return ENOMEM;
1477         arg->na_flags |= NAF_MEM_OWNER;
1478         error = netmap_attach_ext(arg, sizeof(struct netmap_pt_guest_adapter), 1);
1479         if (error)
1480                 return error;
1481
1482         /* get the netmap_pt_guest_adapter */
1483         ptna = (struct netmap_pt_guest_adapter *) NA(ifp);
1484
1485         /* Initialize a separate pass-through netmap adapter that is going to
1486          * be used by the ptnet driver only, and so never exposed to netmap
1487          * applications. We only need a subset of the available fields. */
1488         memset(&ptna->dr, 0, sizeof(ptna->dr));
1489         ptna->dr.up.ifp = ifp;
1490         ptna->dr.up.nm_mem = netmap_mem_get(ptna->hwup.up.nm_mem);
1491         ptna->dr.up.nm_config = ptna->hwup.up.nm_config;
1492
1493         ptna->backend_regifs = 0;
1494
1495         return 0;
1496 }
1497
1498 #endif /* WITH_PTNETMAP_GUEST */