diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2006-05-31 02:09:19 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2006-05-31 02:09:19 +0000 |
commit | 11f7601fe8f6ba0c18c57a83b4e624e78255baed (patch) | |
tree | 9eaf8a9d6f494268cf835ea8f3dad6f630bcf2f3 /sys | |
parent | a3e2b357e4f6852be59de242b7142c44e47ade5c (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.c | 117 |
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. |