summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2013-02-05 00:37:45 +0000
committeryongari <yongari@FreeBSD.org>2013-02-05 00:37:45 +0000
commit4bfa972e11dfe356bbcfbfc980440a356bdb39a9 (patch)
tree6c6e6cdd5a7a53b1850b5a988b35d4ae75ead1e3
parentcfd3c02e72a8574bd02f57cd649cc2a4138d83ef (diff)
downloadFreeBSD-src-4bfa972e11dfe356bbcfbfc980440a356bdb39a9.zip
FreeBSD-src-4bfa972e11dfe356bbcfbfc980440a356bdb39a9.tar.gz
Rework jumbo frame handling. QAC confirmed that the controller
requires 8 bytes alignment on RX buffer. Given that non-jumbo frame works on any alignments I guess this DMA limitation for RX buffer could be jumbo frame specific one. Also I'm not sure whether this DMA limitation is related with 64bit DMA. Previously age(4) disabled 64bit DMA addressing due to silent data corruption. So we may need more testing on re-enabling 64bit DMA in future. While I'm here, change mbuf chaining algorithm to use fixed sized buffer and force software checksum if controller reports length error. According to QAC, RFD is not updated at all for jumbo frame so it works just like alc(4) controllers. This change also added alignment fixup for strict alignment architectures. Because I'm not aware of any non-x86 machines that use age(4) controllers it's just for completeness at this moment. Wit this change, jumbo frame should work with age(4). Tested by: Christian Gusenbauer < c47g <> gmx dot at > MFC after: 1 week
-rw-r--r--sys/dev/age/if_age.c125
-rw-r--r--sys/dev/age/if_agevar.h6
2 files changed, 87 insertions, 44 deletions
diff --git a/sys/dev/age/if_age.c b/sys/dev/age/if_age.c
index 829f350..7fab237 100644
--- a/sys/dev/age/if_age.c
+++ b/sys/dev/age/if_age.c
@@ -142,6 +142,9 @@ static int age_init_rx_ring(struct age_softc *);
static void age_init_rr_ring(struct age_softc *);
static void age_init_cmb_block(struct age_softc *);
static void age_init_smb_block(struct age_softc *);
+#ifndef __NO_STRICT_ALIGNMENT
+static struct mbuf *age_fixup_rx(struct ifnet *, struct mbuf *);
+#endif
static int age_newbuf(struct age_softc *, struct age_rxdesc *);
static void age_rxvlan(struct age_softc *);
static void age_rxfilter(struct age_softc *);
@@ -1133,7 +1136,7 @@ again:
/* Create tag for Rx buffers. */
error = bus_dma_tag_create(
sc->age_cdata.age_buffer_tag, /* parent */
- 1, 0, /* alignment, boundary */
+ AGE_RX_BUF_ALIGN, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
@@ -2268,16 +2271,53 @@ age_txintr(struct age_softc *sc, int tpd_cons)
}
}
+#ifndef __NO_STRICT_ALIGNMENT
+static struct mbuf *
+age_fixup_rx(struct ifnet *ifp, struct mbuf *m)
+{
+ struct mbuf *n;
+ int i;
+ uint16_t *src, *dst;
+
+ src = mtod(m, uint16_t *);
+ dst = src - 3;
+
+ if (m->m_next == NULL) {
+ for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
+ *dst++ = *src++;
+ m->m_data -= 6;
+ return (m);
+ }
+ /*
+ * Append a new mbuf to received mbuf chain and copy ethernet
+ * header from the mbuf chain. This can save lots of CPU
+ * cycles for jumbo frame.
+ */
+ MGETHDR(n, M_NOWAIT, MT_DATA);
+ if (n == NULL) {
+ ifp->if_iqdrops++;
+ m_freem(m);
+ return (NULL);
+ }
+ bcopy(m->m_data, n->m_data, ETHER_HDR_LEN);
+ m->m_data += ETHER_HDR_LEN;
+ m->m_len -= ETHER_HDR_LEN;
+ n->m_len = ETHER_HDR_LEN;
+ M_MOVE_PKTHDR(n, m);
+ n->m_next = m;
+ return (n);
+}
+#endif
+
/* Receive a frame. */
static void
age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
{
struct age_rxdesc *rxd;
- struct rx_desc *desc;
struct ifnet *ifp;
struct mbuf *mp, *m;
uint32_t status, index, vtag;
- int count, nsegs, pktlen;
+ int count, nsegs;
int rx_cons;
AGE_LOCK_ASSERT(sc);
@@ -2289,9 +2329,7 @@ age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
nsegs = AGE_RX_NSEGS(index);
sc->age_cdata.age_rxlen = AGE_RX_BYTES(le32toh(rxrd->len));
- if ((status & AGE_RRD_ERROR) != 0 &&
- (status & (AGE_RRD_CRC | AGE_RRD_CODE | AGE_RRD_DRIBBLE |
- AGE_RRD_RUNT | AGE_RRD_OFLOW | AGE_RRD_TRUNC)) != 0) {
+ if ((status & (AGE_RRD_ERROR | AGE_RRD_LENGTH_NOK)) != 0) {
/*
* We want to pass the following frames to upper
* layer regardless of error status of Rx return
@@ -2301,33 +2339,31 @@ age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
* o frame length and protocol specific length
* does not match.
*/
- sc->age_cdata.age_rx_cons += nsegs;
- sc->age_cdata.age_rx_cons %= AGE_RX_RING_CNT;
- return;
+ status |= AGE_RRD_IPCSUM_NOK | AGE_RRD_TCP_UDPCSUM_NOK;
+ if ((status & (AGE_RRD_CRC | AGE_RRD_CODE | AGE_RRD_DRIBBLE |
+ AGE_RRD_RUNT | AGE_RRD_OFLOW | AGE_RRD_TRUNC)) != 0)
+ return;
}
- pktlen = 0;
for (count = 0; count < nsegs; count++,
AGE_DESC_INC(rx_cons, AGE_RX_RING_CNT)) {
rxd = &sc->age_cdata.age_rxdesc[rx_cons];
mp = rxd->rx_m;
- desc = rxd->rx_desc;
/* Add a new receive buffer to the ring. */
if (age_newbuf(sc, rxd) != 0) {
ifp->if_iqdrops++;
/* Reuse Rx buffers. */
- if (sc->age_cdata.age_rxhead != NULL) {
+ if (sc->age_cdata.age_rxhead != NULL)
m_freem(sc->age_cdata.age_rxhead);
- AGE_RXCHAIN_RESET(sc);
- }
break;
}
- /* The length of the first mbuf is computed last. */
- if (count != 0) {
- mp->m_len = AGE_RX_BYTES(le32toh(desc->len));
- pktlen += mp->m_len;
- }
+ /*
+ * Assume we've received a full sized frame.
+ * Actual size is fixed when we encounter the end of
+ * multi-segmented frame.
+ */
+ mp->m_len = AGE_RX_BUF_SIZE;
/* Chain received mbufs. */
if (sc->age_cdata.age_rxhead == NULL) {
@@ -2342,14 +2378,20 @@ age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
}
if (count == nsegs - 1) {
+ /* Last desc. for this frame. */
+ m = sc->age_cdata.age_rxhead;
+ m->m_flags |= M_PKTHDR;
/*
* It seems that L1 controller has no way
* to tell hardware to strip CRC bytes.
*/
- sc->age_cdata.age_rxlen -= ETHER_CRC_LEN;
+ m->m_pkthdr.len = sc->age_cdata.age_rxlen -
+ ETHER_CRC_LEN;
if (nsegs > 1) {
+ /* Set last mbuf size. */
+ mp->m_len = sc->age_cdata.age_rxlen -
+ ((nsegs - 1) * AGE_RX_BUF_SIZE);
/* Remove the CRC bytes in chained mbufs. */
- pktlen -= ETHER_CRC_LEN;
if (mp->m_len <= ETHER_CRC_LEN) {
sc->age_cdata.age_rxtail =
sc->age_cdata.age_rxprev_tail;
@@ -2360,15 +2402,9 @@ age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
} else {
mp->m_len -= ETHER_CRC_LEN;
}
- }
-
- m = sc->age_cdata.age_rxhead;
- m->m_flags |= M_PKTHDR;
+ } else
+ m->m_len = m->m_pkthdr.len;
m->m_pkthdr.rcvif = ifp;
- m->m_pkthdr.len = sc->age_cdata.age_rxlen;
- /* Set the first mbuf length. */
- m->m_len = sc->age_cdata.age_rxlen - pktlen;
-
/*
* Set checksum information.
* It seems that L1 controller can compute partial
@@ -2383,9 +2419,9 @@ age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
*/
if ((ifp->if_capenable & IFCAP_RXCSUM) != 0 &&
(status & AGE_RRD_IPV4) != 0) {
- m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
if ((status & AGE_RRD_IPCSUM_NOK) == 0)
- m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED | CSUM_IP_VALID;
if ((status & (AGE_RRD_TCP | AGE_RRD_UDP)) &&
(status & AGE_RRD_TCP_UDPCSUM_NOK) == 0) {
m->m_pkthdr.csum_flags |=
@@ -2406,22 +2442,21 @@ age_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
m->m_pkthdr.ether_vtag = AGE_RX_VLAN_TAG(vtag);
m->m_flags |= M_VLANTAG;
}
-
+#ifndef __NO_STRICT_ALIGNMENT
+ m = age_fixup_rx(ifp, m);
+ if (m != NULL)
+#endif
+ {
/* Pass it on. */
AGE_UNLOCK(sc);
(*ifp->if_input)(ifp, m);
AGE_LOCK(sc);
-
- /* Reset mbuf chains. */
- AGE_RXCHAIN_RESET(sc);
+ }
}
}
- if (count != nsegs) {
- sc->age_cdata.age_rx_cons += nsegs;
- sc->age_cdata.age_rx_cons %= AGE_RX_RING_CNT;
- } else
- sc->age_cdata.age_rx_cons = rx_cons;
+ /* Reset mbuf chains. */
+ AGE_RXCHAIN_RESET(sc);
}
static int
@@ -2456,16 +2491,16 @@ age_rxintr(struct age_softc *sc, int rr_prod, int count)
* I'm not sure whether this check is really needed.
*/
pktlen = AGE_RX_BYTES(le32toh(rxrd->len));
- if (nsegs != ((pktlen + (MCLBYTES - ETHER_ALIGN - 1)) /
- (MCLBYTES - ETHER_ALIGN)))
+ if (nsegs != (pktlen + (AGE_RX_BUF_SIZE - 1)) / AGE_RX_BUF_SIZE)
break;
- prog++;
/* Received a frame. */
age_rxeof(sc, rxrd);
/* Clear return ring. */
rxrd->index = 0;
AGE_DESC_INC(rr_cons, AGE_RR_RING_CNT);
+ sc->age_cdata.age_rx_cons += nsegs;
+ sc->age_cdata.age_rx_cons %= AGE_RX_RING_CNT;
}
if (prog > 0) {
@@ -3065,7 +3100,9 @@ age_newbuf(struct age_softc *sc, struct age_rxdesc *rxd)
if (m == NULL)
return (ENOBUFS);
m->m_len = m->m_pkthdr.len = MCLBYTES;
- m_adj(m, ETHER_ALIGN);
+#ifndef __NO_STRICT_ALIGNMENT
+ m_adj(m, AGE_RX_BUF_ALIGN);
+#endif
if (bus_dmamap_load_mbuf_sg(sc->age_cdata.age_rx_tag,
sc->age_cdata.age_rx_sparemap, m, segs, &nsegs, 0) != 0) {
diff --git a/sys/dev/age/if_agevar.h b/sys/dev/age/if_agevar.h
index db98eb1..6ee5423 100644
--- a/sys/dev/age/if_agevar.h
+++ b/sys/dev/age/if_agevar.h
@@ -43,6 +43,12 @@
#define AGE_TSO_MAXSEGSIZE 4096
#define AGE_TSO_MAXSIZE (65535 + sizeof(struct ether_vlan_header))
#define AGE_MAXTXSEGS 32
+#define AGE_RX_BUF_ALIGN 8
+#ifndef __NO_STRICT_ALIGNMENT
+#define AGE_RX_BUF_SIZE (MCLBYTES - AGE_RX_BUF_ALIGN)
+#else
+#define AGE_RX_BUF_SIZE (MCLBYTES)
+#endif
#define AGE_ADDR_LO(x) ((uint64_t) (x) & 0xFFFFFFFF)
#define AGE_ADDR_HI(x) ((uint64_t) (x) >> 32)
OpenPOWER on IntegriCloud