The current umb(4) implementation needs one USB transfer for every packet
that is sent. With the following patch, we can now aggregate several
packets from the ifq into one single USB transfer.

This may speed up the tx path. And even if it doesn't, at least it
reduces the number of transfers required.


Gerhard


Index: sys/dev/usb/if_umb.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_umb.c,v
retrieving revision 1.8
diff -u -p -u -p -r1.8 if_umb.c
--- sys/dev/usb/if_umb.c        25 Nov 2016 12:43:26 -0000      1.8
+++ sys/dev/usb/if_umb.c        12 Dec 2016 13:38:34 -0000
@@ -156,7 +156,7 @@ int          umb_decode_connect_info(struct umb
 int             umb_decode_ip_configuration(struct umb_softc *, void *, int);
 void            umb_rx(struct umb_softc *);
 void            umb_rxeof(struct usbd_xfer *, void *, usbd_status);
-int             umb_encap(struct umb_softc *, struct mbuf *);
+int             umb_encap(struct umb_softc *);
 void            umb_txeof(struct usbd_xfer *, void *, usbd_status);
 void            umb_decap(struct umb_softc *, struct usbd_xfer *);
 
@@ -299,6 +299,7 @@ umb_attach(struct device *parent, struct
 
        sc->sc_udev = uaa->device;
        sc->sc_ctrl_ifaceno = uaa->ifaceno;
+       ml_init(&sc->sc_tx_ml);
 
        /*
         * Some MBIM hardware does not provide the mandatory CDC Union
@@ -583,8 +584,25 @@ umb_ncm_setup(struct umb_softc *sc)
            UGETW(np.wLength) == sizeof (np)) {
                sc->sc_rx_bufsz = UGETDW(np.dwNtbInMaxSize);
                sc->sc_tx_bufsz = UGETDW(np.dwNtbOutMaxSize);
-       } else
+               sc->sc_maxdgram = UGETW(np.wNtbOutMaxDatagrams);
+               sc->sc_align = UGETW(np.wNdpOutAlignment);
+               sc->sc_ndp_div = UGETW(np.wNdpOutDivisor);
+               sc->sc_ndp_remainder = UGETW(np.wNdpOutPayloadRemainder);
+               /* Validate values */
+               if (!powerof2(sc->sc_align) || sc->sc_align == 0 ||
+                   sc->sc_align >= sc->sc_tx_bufsz)
+                       sc->sc_align = sizeof (uint32_t);
+               if (!powerof2(sc->sc_ndp_div) || sc->sc_ndp_div == 0 ||
+                   sc->sc_ndp_div >= sc->sc_tx_bufsz)
+                       sc->sc_ndp_div = sizeof (uint32_t);
+               if (sc->sc_ndp_remainder >= sc->sc_ndp_div)
+                       sc->sc_ndp_remainder = 0;
+       } else {
                sc->sc_rx_bufsz = sc->sc_tx_bufsz = 8 * 1024;
+               sc->sc_maxdgram = 0;
+               sc->sc_align = sc->sc_ndp_div = sizeof (uint32_t);
+               sc->sc_ndp_remainder = 0;
+       }
 }
 
 int
@@ -593,12 +611,12 @@ umb_alloc_xfers(struct umb_softc *sc)
        if (!sc->sc_rx_xfer) {
                if ((sc->sc_rx_xfer = usbd_alloc_xfer(sc->sc_udev)) != NULL)
                        sc->sc_rx_buf = usbd_alloc_buffer(sc->sc_rx_xfer,
-                           sc->sc_rx_bufsz + MBIM_HDR32_LEN);
+                           sc->sc_rx_bufsz);
        }
        if (!sc->sc_tx_xfer) {
                if ((sc->sc_tx_xfer = usbd_alloc_xfer(sc->sc_udev)) != NULL)
                        sc->sc_tx_buf = usbd_alloc_buffer(sc->sc_tx_xfer,
-                           sc->sc_tx_bufsz + MBIM_HDR16_LEN);
+                           sc->sc_tx_bufsz);
        }
        return (sc->sc_rx_buf && sc->sc_tx_buf) ? 1 : 0;
 }
@@ -617,10 +635,7 @@ umb_free_xfers(struct umb_softc *sc)
                sc->sc_tx_xfer = NULL;
                sc->sc_tx_buf = NULL;
        }
-       if (sc->sc_tx_m) {
-               m_freem(sc->sc_tx_m);
-               sc->sc_tx_m = NULL;
-       }
+       ml_purge(&sc->sc_tx_ml);
 }
 
 int
@@ -792,35 +807,91 @@ umb_input(struct ifnet *ifp, struct mbuf
        return 1;
 }
 
+static inline int
+umb_align(size_t bufsz, int offs, int alignment, int remainder)
+{
+       size_t   m = alignment - 1;
+       int      align;
+
+       align = (((size_t)offs + m) & ~m) - alignment + remainder;
+       if (align < offs)
+               align += alignment;
+       if (align > bufsz)
+               align = bufsz;
+       return align - offs;
+}
+
+static inline int
+umb_padding(void *buf, size_t bufsz, int offs, int alignment, int remainder)
+{
+       int      nb;
+
+       nb = umb_align(bufsz, offs, alignment, remainder);
+       if (nb > 0)
+               memset(buf + offs, 0, nb);
+       return nb;
+}
+
 void
 umb_start(struct ifnet *ifp)
 {
        struct umb_softc *sc = ifp->if_softc;
-       struct mbuf *m_head = NULL;
+       struct mbuf *m = NULL;
+       int      ndgram = 0;
+       int      offs, plen, len, mlen;
+       int      maxalign;
 
        if (usbd_is_dying(sc->sc_udev) ||
            !(ifp->if_flags & IFF_RUNNING) ||
            ifq_is_oactive(&ifp->if_snd))
                return;
 
-       m_head = ifq_deq_begin(&ifp->if_snd);
-       if (m_head == NULL)
-               return;
+       KASSERT(ml_empty(&sc->sc_tx_ml));
 
-       if (!umb_encap(sc, m_head)) {
-               ifq_deq_rollback(&ifp->if_snd, m_head);
-               ifq_set_oactive(&ifp->if_snd);
-               return;
-       }
-       ifq_deq_commit(&ifp->if_snd, m_head);
+       offs = sizeof (struct ncm_header16);
+       offs += umb_align(sc->sc_tx_bufsz, offs, sc->sc_align, 0);
+
+       /*
+        * Note that 'struct ncm_pointer16' already includes space for the
+        * terminating zero pointer.
+        */
+       offs += sizeof (struct ncm_pointer16);
+       plen = sizeof (struct ncm_pointer16_dgram);
+       maxalign = (sc->sc_ndp_div - 1) + sc->sc_ndp_remainder;
+       len = 0;
+       while (1) {
+               m = ifq_deq_begin(&ifp->if_snd);
+               if (m == NULL)
+                       break;
+
+               /*
+                * Check if mbuf plus required NCM pointer still fits into
+                * xfer buffers. Assume maximal padding.
+                */
+               plen += sizeof (struct ncm_pointer16_dgram);
+               mlen = maxalign +  m->m_pkthdr.len;
+               if ((sc->sc_maxdgram != 0 && ndgram >= sc->sc_maxdgram) ||
+                   (offs + plen + len + mlen > sc->sc_tx_bufsz)) {
+                       ifq_deq_rollback(&ifp->if_snd, m);
+                       break;
+               }
+               ifq_deq_commit(&ifp->if_snd, m);
+
+               ndgram++;
+               len += mlen;
+               ml_enqueue(&sc->sc_tx_ml, m);
 
 #if NBPFILTER > 0
-       if (ifp->if_bpf)
-               bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
+               if (ifp->if_bpf)
+                       bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
 #endif
-
-       ifq_set_oactive(&ifp->if_snd);
-       ifp->if_timer = (2 * umb_xfer_tout) / 1000;
+       }
+       if (ml_empty(&sc->sc_tx_ml))
+               return;
+       if (umb_encap(sc)) {
+               ifq_set_oactive(&ifp->if_snd);
+               ifp->if_timer = (2 * umb_xfer_tout) / 1000;
+       }
 }
 
 void
@@ -1222,20 +1293,6 @@ umb_getinfobuf(void *in, int inlen, uint
 }
 
 static inline int
-umb_padding(void *data, int len, size_t sz)
-{
-       char    *p = data;
-       int      np = 0;
-
-       while (len < sz && (len % 4) != 0) {
-               *p++ = '\0';
-               len++;
-               np++;
-       }
-       return np;
-}
-
-static inline int
 umb_addstr(void *buf, size_t bufsz, int *offs, void *str, int slen,
     uint32_t *offsmember, uint32_t *sizemember)
 {
@@ -1247,7 +1304,7 @@ umb_addstr(void *buf, size_t bufsz, int 
                *offsmember = htole32((uint32_t)*offs);
                memcpy(buf + *offs, str, slen);
                *offs += slen;
-               *offs += umb_padding(buf, *offs, bufsz);
+               *offs += umb_padding(buf, bufsz, *offs, sizeof (uint32_t), 0);
        } else
                *offsmember = htole32(0);
        return 1;
@@ -1706,46 +1763,71 @@ umb_rxeof(struct usbd_xfer *xfer, void *
 }
 
 int
-umb_encap(struct umb_softc *sc, struct mbuf *m)
+umb_encap(struct umb_softc *sc)
 {
        struct ncm_header16 *hdr;
        struct ncm_pointer16 *ptr;
+       struct ncm_pointer16_dgram *dgram;
+       int      offs, poffs;
+       struct mbuf_list tmpml = MBUF_LIST_INITIALIZER();
+       struct mbuf *m;
        usbd_status  err;
-       int      len;
-
-       KASSERT(sc->sc_tx_m == NULL);
 
+       /* All size constraints have been validated by the caller! */
        hdr = sc->sc_tx_buf;
-       ptr = (struct ncm_pointer16 *)(hdr + 1);
-
        USETDW(hdr->dwSignature, NCM_HDR16_SIG);
        USETW(hdr->wHeaderLength, sizeof (*hdr));
+       USETW(hdr->wBlockLength, 0);
        USETW(hdr->wSequence, sc->sc_tx_seq);
        sc->sc_tx_seq++;
-       USETW(hdr->wNdpIndex, sizeof (*hdr));
+       offs = sizeof (*hdr);
+       offs += umb_padding(sc->sc_tx_buf, sc->sc_tx_bufsz, offs,
+           sc->sc_align, 0);
+       USETW(hdr->wNdpIndex, offs);
 
-       len = m->m_pkthdr.len;
+       poffs = offs;
+       ptr = (struct ncm_pointer16 *)(sc->sc_tx_buf + offs);
        USETDW(ptr->dwSignature, MBIM_NCM_NTH16_SIG(umb_session_id));
-       USETW(ptr->wLength, sizeof (*ptr));
        USETW(ptr->wNextNdpIndex, 0);
-       USETW(ptr->dgram[0].wDatagramIndex, MBIM_HDR16_LEN);
-       USETW(ptr->dgram[0].wDatagramLen, len);
-       USETW(ptr->dgram[1].wDatagramIndex, 0);
-       USETW(ptr->dgram[1].wDatagramLen, 0);
-
-       m_copydata(m, 0, len, (caddr_t)(ptr + 1));
-       sc->sc_tx_m = m;
-       len += MBIM_HDR16_LEN;
-       USETW(hdr->wBlockLength, len);
-
-       DPRINTFN(3, "%s: encap %d bytes\n", DEVNAM(sc), len);
-       DDUMPN(5, sc->sc_tx_buf, len);
-       usbd_setup_xfer(sc->sc_tx_xfer, sc->sc_tx_pipe, sc, sc->sc_tx_buf, len,
+       dgram = &ptr->dgram[0];
+       offs = (caddr_t)dgram - (caddr_t)sc->sc_tx_buf;
+
+       /* Leave space for dgram pointers */
+       while ((m = ml_dequeue(&sc->sc_tx_ml)) != NULL) {
+               offs += sizeof (*dgram);
+               ml_enqueue(&tmpml, m);
+       }
+       offs += sizeof (*dgram);        /* one more to terminate pointer list */
+       USETW(ptr->wLength, offs - poffs);
+
+       /* Encap mbufs */
+       while ((m = ml_dequeue(&tmpml)) != NULL) {
+               offs += umb_padding(sc->sc_tx_buf, sc->sc_tx_bufsz, offs,
+                   sc->sc_ndp_div, sc->sc_ndp_remainder);
+               USETW(dgram->wDatagramIndex, offs);
+               USETW(dgram->wDatagramLen, m->m_pkthdr.len);
+               dgram++;
+               m_copydata(m, 0, m->m_pkthdr.len, sc->sc_tx_buf + offs);
+               offs += m->m_pkthdr.len;
+               ml_enqueue(&sc->sc_tx_ml, m);
+       }
+
+       /* Terminating pointer */
+       USETW(dgram->wDatagramIndex, 0);
+       USETW(dgram->wDatagramLen, 0);
+       USETW(hdr->wBlockLength, offs);
+
+       DPRINTFN(3, "%s: encap %d bytes\n", DEVNAM(sc), offs);
+       DDUMPN(5, sc->sc_tx_buf, offs);
+       KASSERT(offs <= sc->sc_tx_bufsz);
+
+       usbd_setup_xfer(sc->sc_tx_xfer, sc->sc_tx_pipe, sc, sc->sc_tx_buf, offs,
            USBD_FORCE_SHORT_XFER | USBD_NO_COPY, umb_xfer_tout, umb_txeof);
        err = usbd_transfer(sc->sc_tx_xfer);
        if (err != USBD_IN_PROGRESS) {
                DPRINTF("%s: start tx error: %s\n", DEVNAM(sc),
                    usbd_errstr(err));
+               ml_purge(&sc->sc_tx_ml);
                return 0;
        }
        return 1;
@@ -1759,12 +1841,10 @@ umb_txeof(struct usbd_xfer *xfer, void *
        int      s;
 
        s = splnet();
+       ml_purge(&sc->sc_tx_ml);
        ifq_clr_oactive(&ifp->if_snd);
        ifp->if_timer = 0;
 
-       m_freem(sc->sc_tx_m);
-       sc->sc_tx_m = NULL;
-
        if (status != USBD_NORMAL_COMPLETION) {
                if (status != USBD_NOT_STARTED && status != USBD_CANCELLED) {
                        ifp->if_oerrors++;
@@ -1814,10 +1894,7 @@ umb_decap(struct umb_softc *sc, struct u
        hlen = UGETW(hdr16->wHeaderLength);
        if (len < hlen)
                goto toosmall;
-       if (len > sc->sc_rx_bufsz) {
-               DPRINTF("%s: packet too large (%d)\n", DEVNAM(sc), len);
-               goto fail;
-       }
+
        switch (hsig) {
        case NCM_HDR16_SIG:
                blen = UGETW(hdr16->wBlockLength);
@@ -1843,7 +1920,7 @@ umb_decap(struct umb_softc *sc, struct u
                    DEVNAM(sc), hsig);
                goto fail;
        }
-       if (len < blen) {
+       if (blen != 0 && len < blen) {
                DPRINTF("%s: bad NTB len (%d) for %d bytes of data\n",
                    DEVNAM(sc), blen, len);
                goto fail;
Index: sys/dev/usb/if_umb.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_umb.h,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 if_umb.h
--- sys/dev/usb/if_umb.h        25 Nov 2016 12:43:26 -0000      1.3
+++ sys/dev/usb/if_umb.h        12 Dec 2016 13:38:34 -0000
@@ -339,6 +339,11 @@ struct umb_softc {
        int                      sc_maxpktlen;
        int                      sc_maxsessions;
 
+       int                      sc_maxdgram;
+       int                      sc_align;
+       int                      sc_ndp_div;
+       int                      sc_ndp_remainder;
+
 #define UMBFLG_FCC_AUTH_REQUIRED       0x0001
        uint32_t                 sc_flags;
        int                      sc_cid;
@@ -368,7 +373,7 @@ struct umb_softc {
        void                    *sc_tx_buf;
        int                      sc_tx_bufsz;
        struct usbd_pipe        *sc_tx_pipe;
-       struct mbuf             *sc_tx_m;
+       struct mbuf_list         sc_tx_ml;
        uint32_t                 sc_tx_seq;
 
        uint32_t                 sc_tid;
Index: sys/dev/usb/mbim.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/mbim.h,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 mbim.h
--- sys/dev/usb/mbim.h  25 Nov 2016 12:43:26 -0000      1.3
+++ sys/dev/usb/mbim.h  12 Dec 2016 13:38:34 -0000
@@ -612,25 +612,20 @@ struct ncm_ntb_parameters {
 #define NCM_FORMAT_NTB16       0x0001
 #define NCM_FORMAT_NTB32       0x0002
        uDWord  dwNtbInMaxSize;
-       uWord   wNtbInDivisor;
-       uWord   wNtbInPayloadRemainder;
-       uWord   wNtbInAlignment;
+       uWord   wNdpInDivisor;
+       uWord   wNdpInPayloadRemainder;
+       uWord   wNdpInAlignment;
        uWord   wReserved1;
        uDWord  dwNtbOutMaxSize;
-       uWord   wNtbOutDivisor;
-       uWord   wNtbOutPayloadRemainder;
-       uWord   wNtbOutAlignment;
+       uWord   wNdpOutDivisor;
+       uWord   wNdpOutPayloadRemainder;
+       uWord   wNdpOutAlignment;
        uWord   wNtbOutMaxDatagrams;
 } __packed;
 
 /*
  * NCM Encoding
  */
-#define MBIM_HDR16_LEN         \
-       (sizeof (struct ncm_header16) + sizeof (struct ncm_pointer16))
-#define MBIM_HDR32_LEN         \
-       (sizeof (struct ncm_header32) + sizeof (struct ncm_pointer32))
-
 struct ncm_header16 {
 #define NCM_HDR16_SIG          0x484d434e
        uDWord  dwSignature;
@@ -668,7 +663,7 @@ struct ncm_pointer16 {
        uWord   wNextNdpIndex;
 
        /* Minimum is two datagrams, but can be more */
-       struct ncm_pointer16_dgram dgram[2];
+       struct ncm_pointer16_dgram dgram[1];
 } __packed;
 
 struct ncm_pointer32_dgram {
@@ -689,7 +684,7 @@ struct ncm_pointer32 {
        uDWord  dwReserved12;
 
        /* Minimum is two datagrams, but can be more */
-       struct ncm_pointer32_dgram dgram[2];
+       struct ncm_pointer32_dgram dgram[1];
 } __packed;
 
 #endif /* _KERNEL */

Reply via email to