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