I'm not really expecting tun to provide blistering performance, but
one of the things that keeps it slower than need be is the one packet
per read/write implementation. It'd be pretty cool to have
something like readv() or writev() provide the ability to operate on
multiple packets at once. Unfortunately the v interfaces don't provide
quite enough information, but we can create a new one with ioctl.

The TUNDOIOVEC ioctl takes a struct consisting of two iov arrays, one
for read and one for write, and the relevant counts. One syscall will
(without blocking) receive and send up to 8 packets in each direction.
Then it updates the structure so userland knows how many packets went
each way.

Thoughts?

Index: if_tun.c
===================================================================
RCS file: /cvs/src/sys/net/if_tun.c,v
retrieving revision 1.125
diff -u -p -r1.125 if_tun.c
--- if_tun.c    5 May 2014 11:44:33 -0000       1.125
+++ if_tun.c    10 Jul 2014 20:11:27 -0000
@@ -132,6 +132,8 @@ void        filt_tunwdetach(struct knote *);
 void   tunstart(struct ifnet *);
 void   tun_link_state(struct tun_softc *);
 
+int tuncopyout(struct ifnet *ifp, struct mbuf *m0, struct uio *uio);
+
 struct filterops tunread_filtops =
        { 1, NULL, filt_tunrdetach, filt_tunread};
 
@@ -603,10 +605,13 @@ tun_wakeup(struct tun_softc *tp)
 int
 tunioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
 {
-       int                      s;
+       int                     i, s, ret;
        struct tun_softc        *tp;
+       struct ifnet            *ifp;
        struct tuninfo          *tunp;
+       struct tuniovec         *tiov;
        struct mbuf             *m;
+       struct uio              uio;
 
        if ((tp = tun_lookup(minor(dev))) == NULL)
                return (ENXIO);
@@ -699,10 +704,47 @@ tunioctl(dev_t dev, u_long cmd, caddr_t 
                bcopy(data, tp->arpcom.ac_enaddr,
                    sizeof(tp->arpcom.ac_enaddr));
                break;
+       case TUNDOIOVEC:
+               splx(s);
+               ifp = &tp->tun_if;
+               tiov = (struct tuniovec *)data;
+               for (i = 0; i < tiov->readcnt; i++) {
+                       s = splnet();
+                       if ((tp->tun_flags & TUN_READY) != TUN_READY)
+                               break;
+                       IFQ_DEQUEUE(&ifp->if_snd, m);
+                       splx(s);
+                       if (m == NULL)
+                               break;
+                       uio.uio_iov = &tiov->readiov[i];
+                       uio.uio_iovcnt = 1;
+                       uio.uio_offset = 0;
+                       uio.uio_resid = tiov->readiov[i].iov_len;
+                       uio.uio_segflg = UIO_USERSPACE;
+                       uio.uio_rw = UIO_READ;
+                       uio.uio_procp = p;
+                       ret = tuncopyout(ifp, m, &uio);
+                       if (ret != 0)
+                               break;
+               }
+               tiov->readcnt = i;
+               for (i = 0; i < tiov->writecnt; i++) {
+                       uio.uio_iov = &tiov->writeiov[i];
+                       uio.uio_iovcnt = 1;
+                       uio.uio_offset = 0;
+                       uio.uio_resid = tiov->writeiov[i].iov_len;
+                       uio.uio_segflg = UIO_USERSPACE;
+                       uio.uio_rw = UIO_WRITE;
+                       uio.uio_procp = p;
+                       ret = tunwrite(dev, &uio, 0);
+                       if (ret != 0)
+                               break;
+               }
+               tiov->writecnt = i;
+               return (0);
        default:
 #ifdef PIPEX
            {
-               int ret;
                ret = pipex_ioctl(&tp->pipex_iface, cmd, data);
                splx(s);
                return (ret);
@@ -725,8 +767,8 @@ tunread(dev_t dev, struct uio *uio, int 
 {
        struct tun_softc        *tp;
        struct ifnet            *ifp;
-       struct mbuf             *m, *m0;
-       int                      error = 0, len, s;
+       struct mbuf             *m0;
+       int                      error = 0, s;
 
        if ((tp = tun_lookup(minor(dev))) == NULL)
                return (ENXIO);
@@ -763,6 +805,15 @@ tunread(dev_t dev, struct uio *uio, int 
                }
        } while (m0 == NULL);
        splx(s);
+
+       return tuncopyout(ifp, m0, uio);
+}
+
+int
+tuncopyout(struct ifnet *ifp, struct mbuf *m0, struct uio *uio)
+{
+       struct mbuf *m;
+       int error, len;
 
        while (m0 != NULL && uio->uio_resid > 0 && error == 0) {
                len = min(uio->uio_resid, m0->m_len);
Index: if_tun.h
===================================================================
RCS file: /cvs/src/sys/net/if_tun.h,v
retrieving revision 1.15
diff -u -p -r1.15 if_tun.h
--- if_tun.h    6 Feb 2007 10:49:40 -0000       1.15
+++ if_tun.h    10 Jul 2014 19:47:40 -0000
@@ -68,4 +68,13 @@ struct tuninfo {
 #define        TUNSDEBUG       _IOW('t', 94, int)
 #define        TUNGDEBUG       _IOR('t', 95, int)
 
+#define TUNMAXIOS 8
+struct tuniovec {
+       int readcnt;
+       struct iovec readiov[TUNMAXIOS];
+       int writecnt;
+       struct iovec writeiov[TUNMAXIOS];
+};
+#define        TUNDOIOVEC      _IOWR('t', 96, struct tuniovec)
+
 #endif /* _NET_IF_TUN_H_ */

Reply via email to