adjust rx path:
- do not set RXCTL_IPE, thus eliminating padding bytes on each
  packet
- switch from MGETHDR/MCLGET/memcpy to m_getdev
- improve buffer length and packet checks

---
 dev/usb/if_axen.c    | 172 +++++++++++++++++++++++--------------------
 dev/usb/if_axenreg.h |   4 +
 2 files changed, 95 insertions(+), 81 deletions(-)

diff --git a/dev/usb/if_axen.c b/dev/usb/if_axen.c
index 190bf2e..909c04f 100644
--- a/dev/usb/if_axen.c
+++ b/dev/usb/if_axen.c
@@ -97,7 +97,6 @@ const struct cfattach axen_ca = {
 
 int    axen_tx_list_init(struct axen_softc *);
 int    axen_rx_list_init(struct axen_softc *);
-struct mbuf *axen_newbuf(void);
 int    axen_encap(struct axen_softc *, struct mbuf *, int);
 void   axen_rxeof(struct usbd_xfer *, void *, usbd_status);
 void   axen_txeof(struct usbd_xfer *, void *, usbd_status);
@@ -535,7 +534,7 @@ axen_ax88179_init(struct axen_softc *sc)
        axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_TX_COE, &val);
 
        /* Set RX control register */
-       ctl = AXEN_RXCTL_IPE | AXEN_RXCTL_DROPCRCERR | AXEN_RXCTL_AUTOB;
+       ctl = AXEN_RXCTL_DROPCRCERR | AXEN_RXCTL_AUTOB;
        ctl |= AXEN_RXCTL_ACPT_PHY_MCAST | AXEN_RXCTL_ACPT_ALL_MCAST;
        ctl |= AXEN_RXCTL_START;
        USETW(wval, ctl);
@@ -787,27 +786,6 @@ axen_detach(struct device *self, int flags)
        return 0;
 }
 
-struct mbuf *
-axen_newbuf(void)
-{
-       struct mbuf *m;
-
-       MGETHDR(m, M_DONTWAIT, MT_DATA);
-       if (m == NULL)
-               return NULL;
-
-       MCLGET(m, M_DONTWAIT);
-       if (!(m->m_flags & M_EXT)) {
-               m_freem(m);
-               return NULL;
-       }
-
-       m->m_len = m->m_pkthdr.len = MCLBYTES;
-       m_adj(m, ETHER_ALIGN);
-
-       return m;
-}
-
 int
 axen_rx_list_init(struct axen_softc *sc)
 {
@@ -885,11 +863,13 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
        struct mbuf             *m;
        u_int32_t               total_len;
        u_int32_t               rx_hdr, pkt_hdr;
-       u_int32_t               *hdr_p;
+       u_int32_t               *hdr_p, *pkt_end, *hdr_end;
        u_int16_t               hdr_offset, pkt_count;
        size_t                  pkt_len;
-       size_t                  temp;
        int                     s;
+#ifdef AXEN_DEBUG
+       u_int16_t               total_pkt_count;
+#endif
 
        DPRINTFN(10,("%s: %s: enter\n", sc->axen_dev.dv_xname,__func__));
 
@@ -913,56 +893,72 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
 
        usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
 
-       if (total_len < sizeof(pkt_hdr)) {
+       if (total_len < sizeof(rx_hdr)) {
+               printf("%s: rxeof: too short transfer (len %u)\n",
+                   sc->axen_dev.dv_xname, total_len);
+               ifp->if_ierrors++;
+               goto done;
+       }
+
+       if (total_len > sc->axen_bufsz) {
+               printf("%s: rxeof: too large transfer (len %u)\n",
+                   sc->axen_dev.dv_xname, total_len);
                ifp->if_ierrors++;
                goto done;
        }
 
-       /* 
-        * buffer map
-        * [packet #0]...[packet #n][pkt hdr#0]..[pkt hdr#n][recv_hdr]
-        * each packet has 0xeeee as psuedo header..
+       /*
+        * <--------------------- total_len -------------------->
+        * [pkt #0]...[pkt #n][pkt_hdr #0]...[pkt_hdr #n][rx_hdr]
+        * ^buf               ^pkt_end                   ^hdr_end
+        *
+        * Each RX frame is aligned on 8 bytes boundary. If RXCTL_IPE
+        * bit is set in MAC_RXCTL register, there would be 2
+        * padding bytes and 6 dummy bytes(as the padding also should
+        * be aligned on 8 bytes boundary) for each RX frame to align
+        * IP header on 32bits boundary. We don't set RXCTL_IPE bit
+        * of MAC_RXCTL register, so there should be no padding bytes
+        * which simplifies RX logic a lot.
         */
-       hdr_p = (u_int32_t *)(buf + total_len - sizeof(u_int32_t));
-       rx_hdr = letoh32(*hdr_p);
+
+       hdr_end = (u_int32_t *)(buf + total_len - sizeof(rx_hdr));
+       rx_hdr = le32toh(*hdr_end);
        hdr_offset = (u_int16_t)(rx_hdr >> 16);
        pkt_count  = (u_int16_t)(rx_hdr & 0xffff);
 
-       if (total_len > sc->axen_bufsz) {
-               printf("%s: rxeof: too large transfer\n",
-                   sc->axen_dev.dv_xname);
-               goto done;
-       }
+#ifdef AXEN_DEBUG
+       total_pkt_count = pkt_count;
+#endif
 
        /* sanity check */
-       if (hdr_offset > total_len) {
+       if (hdr_offset > total_len - sizeof(rx_hdr)) {
                ifp->if_ierrors++;
                goto done;
        }
 
        /* point first packet header */
-       hdr_p = (u_int32_t*)(buf + hdr_offset);
-
-       /*
-        * ax88179 will pack multiple ip packet to a USB transaction.
-        * process all of packets in the buffer
-        */
+       hdr_p = pkt_end = (u_int32_t *)(buf + hdr_offset);
 
-#if 1 /* XXX: paranoiac check. need to remove later */
-#define AXEN_MAX_PACKED_PACKET 200 
+#ifdef AXEN_DEBUG
+#define AXEN_MAX_PACKED_PACKET 200
        if (pkt_count > AXEN_MAX_PACKED_PACKET) {
                DPRINTF(("Too many packets (%d) in a transaction, discard.\n", 
                    pkt_count));
+               ifp->if_ierrors += pkt_count;
                goto done;
        }
 #endif
-
-       do {
-               if ((buf[0] != 0xee) || (buf[1] != 0xee)){
-                       printf("%s: invalid buffer(pkt#%d), continue\n",
-                           sc->axen_dev.dv_xname, pkt_count);
-                       ifp->if_ierrors += pkt_count;
-                       goto done;
+       /*
+        * ax88179 will pack multiple ip packet to a USB transaction.
+        * process all of packets in the buffer
+        */
+       while(pkt_count--) {
+               /* verify the header offset */
+               if (hdr_p >= hdr_end) {
+                       DPRINTF(("%s: %s: end of packet headers(pkt#%d)\n",
+                           sc->axen_dev.dv_xname,__func__,pkt_count));
+                       ifp->if_ierrors += pkt_count + 1;
+                       goto input;
                }
 
                pkt_hdr = letoh32(*hdr_p);
@@ -971,24 +967,36 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
                DPRINTFN(10,("rxeof: packet#%d, pkt_hdr 0x%08x, pkt_len %zu\n",
                   pkt_count, pkt_hdr, pkt_len));
 
-               if ((pkt_hdr & AXEN_RXHDR_CRC_ERR) ||
-                   (pkt_hdr & AXEN_RXHDR_DROP_ERR)) {
-                       ifp->if_ierrors++;
-                       /* move to next pkt header */
-                       DPRINTF(("crc err(pkt#%d)\n", pkt_count));
-                       goto nextpkt;
+               /* verify the data length */
+               if ((void *)buf + pkt_len > (void *)hdr_p) {
+                       DPRINTF(("%s: %s: end of packet data(pkt#%d)\n",
+                           sc->axen_dev.dv_xname,__func__,pkt_count));
+                       ifp->if_ierrors += pkt_count + 1;
+                       goto input;
                }
 
-               /* process each packet */
-               /* allocate mbuf */
-               m = axen_newbuf();
-               if (m == NULL) {
+               if (pkt_len > MCLBYTES || pkt_len < ETHER_MIN_LEN) {
+                       printf("%s: invalid pkt_len %zu\n",
+                           sc->axen_dev.dv_xname,pkt_len);
                        ifp->if_ierrors++;
                        goto nextpkt;
                }
 
-               /* skip pseudo header (2byte) and trailer padding (4Byte) */
-               m->m_pkthdr.len = m->m_len = pkt_len - 6;
+               if (AXEN_RXHDR_ERR(pkt_hdr)) {
+#ifdef AXEN_DEBUG
+                       DPRINTFN(7,("%s: %s: err(pkt#%d) pkt_hdr 0x%08x,",
+                           sc->axen_dev.dv_xname,__func__,pkt_count,pkt_hdr));
+                       if (pkt_hdr & AXEN_RXHDR_DROP_ERR)
+                               DPRINTFN(7,(" drop"));
+                       if (pkt_hdr & AXEN_RXHDR_MII_ERR)
+                               DPRINTFN(7,(" mii"));
+                       if (pkt_hdr & AXEN_RXHDR_CRC_ERR)
+                               DPRINTFN(7,(" crc"));
+                       DPRINTFN(7,("\n"));
+#endif
+                       ifp->if_ierrors++;
+                       goto nextpkt;
+               }
 
 #ifdef AXEN_TOE
                /* cheksum err */
@@ -1000,18 +1008,21 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
                } else {
                        m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
                }
-
-               int l4_type;
-               l4_type = (pkt_hdr & AXEN_RXHDR_L4_TYPE_MASK) >> 
-                   AXEN_RXHDR_L4_TYPE_OFFSET;
-
-               if ((l4_type == AXEN_RXHDR_L4_TYPE_TCP) ||
-                   (l4_type == AXEN_RXHDR_L4_TYPE_UDP)) 
-                       m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
-                           M_UDP_CSUM_IN_OK;
 #endif
 
-               memcpy(mtod(m, char *), buf + 2, pkt_len - 6);
+               /* process each packet */
+               /* allocate mbuf */
+               m = m_devget(buf, pkt_len, ETHER_ALIGN);
+               if (m == NULL) {
+#ifdef AXEN_DEBUG
+                       DPRINTF(("%s: could not allocate rx mbuf of size %zu "
+                           "(%d total, %d remaining)\n",
+                           sc->axen_dev.dv_xname, pkt_len,
+                           total_pkt_count, pkt_count + 1));
+#endif
+                       ifp->if_ierrors += pkt_count + 1;
+                       goto input;
+               }
 
                ml_enqueue(&ml, m);
 
@@ -1021,18 +1032,17 @@ nextpkt:
                 * as each packet will be aligned 8byte boundary,
                 * need to fix up the start point of the buffer.
                 */
-               temp = ((pkt_len + 7) & 0xfff8);
-               buf = buf + temp;
+               buf += (pkt_len + 7) & 0xfff8;
                hdr_p++;
-               pkt_count--;
-       } while( pkt_count > 0);
+       }
 
-done:
-       /* push the packet up */
+input:
+       /* push the packets up */
        s = splnet();
        if_input(ifp, &ml);
        splx(s);
 
+done:
        /* clear buffer for next transaction */
        memset(c->axen_buf, 0, sc->axen_bufsz);
 
diff --git a/dev/usb/if_axenreg.h b/dev/usb/if_axenreg.h
index 8de2c6d..bbfb035 100644
--- a/dev/usb/if_axenreg.h
+++ b/dev/usb/if_axenreg.h
@@ -62,6 +62,10 @@
 #define AXEN_RXHDR_L3CSUM_ERR  (1U << 1)
 #define AXEN_RXHDR_L4CSUM_ERR  (1U << 0)
 
+#define        AXEN_RXHDR_ERR(x)       ((x) & (AXEN_RXHDR_DROP_ERR | \
+                                       AXEN_RXHDR_MII_ERR | \
+                                       AXEN_RXHDR_CRC_ERR))
+
 /* L4 packet type (3bit) */
 #define AXEN_RXHDR_L4_TYPE_MASK        0x0000001c
 #define AXEN_RXHDR_L4_TYPE_OFFSET      2
-- 
2.20.1

Reply via email to