summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorBrad Smith <brad@cvs.openbsd.org>2006-05-31 02:09:19 +0000
committerBrad Smith <brad@cvs.openbsd.org>2006-05-31 02:09:19 +0000
commit11f7601fe8f6ba0c18c57a83b4e624e78255baed (patch)
tree9eaf8a9d6f494268cf835ea8f3dad6f630bcf2f3 /sys
parenta3e2b357e4f6852be59de242b7142c44e47ade5c (diff)
fix Jumbo frames on strict alignment architectures by allocating a new mbuf and
copying the Ethernet header to the new mbuf. The new mbuf is then prepended into the existing mbuf chain. From FreeBSD ok reyk@ pascoe@ jason@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/if_em.c117
1 files changed, 56 insertions, 61 deletions
diff --git a/sys/dev/pci/if_em.c b/sys/dev/pci/if_em.c
index b2f4fb72cd7..253199aeb92 100644
--- a/sys/dev/pci/if_em.c
+++ b/sys/dev/pci/if_em.c
@@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
-/* $OpenBSD: if_em.c,v 1.130 2006/05/28 23:38:49 brad Exp $ */
+/* $OpenBSD: if_em.c,v 1.131 2006/05/31 02:09:18 brad Exp $ */
/* $FreeBSD: if_em.c,v 1.46 2004/09/29 18:28:28 mlaier Exp $ */
#include <dev/pci/if_em.h>
@@ -143,6 +143,9 @@ void em_clean_transmit_interrupts(struct em_softc *);
int em_allocate_receive_structures(struct em_softc *);
int em_allocate_transmit_structures(struct em_softc *);
void em_process_receive_interrupts(struct em_softc *, int);
+#ifdef __STRICT_ALIGNMENT
+int em_fixup_rx(struct em_softc *);
+#endif
void em_receive_checksum(struct em_softc *, struct em_rx_desc *,
struct mbuf *);
void em_transmit_checksum_setup(struct em_softc *, struct mbuf *,
@@ -265,9 +268,6 @@ em_attach(struct device *parent, struct device *self, void *aux)
* Set the max frame size assuming standard Ethernet
* sized frames.
*/
-#ifdef __STRICT_ALIGNMENT
- sc->hw.max_frame_size = ETHER_MAX_LEN;
-#else
switch (sc->hw.mac_type) {
case em_82573:
{
@@ -294,7 +294,6 @@ em_attach(struct device *parent, struct device *self, void *aux)
sc->hw.max_frame_size =
MAX_JUMBO_FRAME_SIZE;
}
-#endif
sc->hw.min_frame_size =
ETHER_MIN_LEN + ETHER_CRC_LEN;
@@ -2051,11 +2050,6 @@ em_get_buf(int i, struct em_softc *sc, struct mbuf *nmp)
mp->m_next = NULL;
}
-#ifdef __STRICT_ALIGNMENT
- if (ifp->if_mtu <= ETHERMTU)
- m_adj(mp, ETHER_ALIGN);
-#endif
-
rx_buffer = &sc->rx_buffer_area[i];
/*
@@ -2225,10 +2219,8 @@ em_initialize_receive_unit(struct em_softc *sc)
break;
}
-#ifndef __STRICT_ALIGNMENT
if (sc->hw.mac_type != em_82573)
reg_rctl |= E1000_RCTL_LPE;
-#endif
/* Enable 82543 Receive Checksum Offload for TCP and UDP */
if (sc->hw.mac_type >= em_82543) {
@@ -2368,55 +2360,6 @@ em_process_receive_interrupts(struct em_softc *sc, int count)
/* Assign correct length to the current fragment */
mp->m_len = len;
-#ifdef __STRICT_ALIGNMENT
- /*
- * The Ethernet payload is not 32-bit aligned when
- * Jumbo packets are enabled, so on architectures with
- * strict alignment we need to shift the entire packet
- * ETHER_ALIGN bytes. Ugh.
- */
- if (ifp->if_mtu > ETHERMTU) {
- unsigned char tmp_align_buf[ETHER_ALIGN];
- int tmp_align_buf_len = 0;
-
- if (prev_len_adj > sc->align_buf_len)
- prev_len_adj -= sc->align_buf_len;
- else
- prev_len_adj = 0;
-
- if (mp->m_len > MCLBYTES - ETHER_ALIGN) {
- bcopy(mp->m_data +
- (MCLBYTES - ETHER_ALIGN),
- &tmp_align_buf,
- ETHER_ALIGN);
- tmp_align_buf_len = mp->m_len -
- (MCLBYTES - ETHER_ALIGN);
- mp->m_len -= ETHER_ALIGN;
- }
-
- if (mp->m_len) {
- bcopy(mp->m_data,
- mp->m_data + ETHER_ALIGN,
- mp->m_len);
- if (!sc->align_buf_len)
- mp->m_data += ETHER_ALIGN;
- }
-
- if (sc->align_buf_len) {
- mp->m_len += sc->align_buf_len;
- bcopy(&sc->align_buf,
- mp->m_data,
- sc->align_buf_len);
- }
-
- if (tmp_align_buf_len)
- bcopy(&tmp_align_buf,
- &sc->align_buf,
- tmp_align_buf_len);
- sc->align_buf_len = tmp_align_buf_len;
- }
-#endif /* __STRICT_ALIGNMENT */
-
if (sc->fmp == NULL) {
mp->m_pkthdr.len = mp->m_len;
sc->fmp = mp; /* Store the first mbuf */
@@ -2442,6 +2385,9 @@ em_process_receive_interrupts(struct em_softc *sc, int count)
ifp->if_ipackets++;
em_receive_checksum(sc, current_desc,
sc->fmp);
+#ifdef __STRICT_ALIGNMENT
+ em_fixup_rx(sc);
+#endif
m = sc->fmp;
sc->fmp = NULL;
sc->lmp = NULL;
@@ -2489,6 +2435,55 @@ em_process_receive_interrupts(struct em_softc *sc, int count)
E1000_WRITE_REG(&sc->hw, RDT, i);
}
+
+
+#ifdef __STRICT_ALIGNMENT
+/*
+ * When Jumbo frames are enabled we should realign the entire payload on
+ * strict alignment architecures. This is a serious design mistake of the
+ * 8254x chipset as it nullifies DMA operations. 8254x allows the RX buffer
+ * size to be 2048/4096/8192/16384. What we really want is 2048 - ETHER_ALIGN
+ * to align its payload. On non strict alignment architectures 8254x still
+ * performs unaligned memory access which will reduce the performance too. To
+ * avoid copying over an entire frame to align, we allocate a new mbuf and
+ * copy the Ethernet header to the new mbuf. The new mbuf is then prepended
+ * into the existing mbuf chain.
+ *
+ * Be aware, best performance of the 8254x chipset is achived only when Jumbo
+ * frames are not used at all on strict alignment architectures.
+ */
+int
+em_fixup_rx(struct em_softc *sc)
+{
+ struct mbuf *m, *n;
+ int error = 0;
+
+ m = sc->fmp;
+ if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) {
+ bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len);
+ m->m_data += ETHER_HDR_LEN;
+ } else {
+ MGETHDR(n, M_DONTWAIT, MT_DATA);
+ if (n != 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;
+ sc->fmp = n;
+ } else {
+ sc->dropped_pkts++;
+ m_freem(sc->fmp);
+ sc->fmp = NULL;
+ error = ENOMEM;
+ }
+ }
+
+ return (error);
+}
+#endif
+
/*********************************************************************
*
* Verify that the hardware indicated that the checksum is valid.