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