Gianfar TCP checksumming broken in 2.6.35+
Matthew L. Creech
mlcreech at gmail.com
Fri Nov 19 06:31:46 EST 2010
On Thu, Nov 18, 2010 at 12:06 PM, David Miller <davem at davemloft.net> wrote:
>
> Can someone please follow up Matthew to get this bug resolved? It has
> been sitting around for a long time.
>
> I suspect the gianfar driver, for these chip revisions, will need to
> do a software checksum when the offset matches the criteria mentioned
> in the errata above.
>
I added a patch for this which fixes our affected systems; however, I
don't know if this is a good way to perform checksum offloading, I
just kind of dug around until I found a function that seemed like it
worked. :) Patch against 2.6.36 is below for reference.
---
gianfar.c | 21 +++++++++++++++++++--
gianfar.h | 1 +
2 files changed, 20 insertions(+), 2 deletions(-)
diff -purN orig/drivers/net/gianfar.c linux-2.6.36/drivers/net/gianfar.c
--- orig/drivers/net/gianfar.c 2010-11-03 15:10:29.287140651 -0400
+++ linux-2.6.36/drivers/net/gianfar.c 2010-11-03 16:01:03.754321896 -0400
@@ -937,6 +937,10 @@ static void gfar_detect_errata(struct gf
unsigned int mod = (svr >> 16) & 0xfff6; /* w/o E suffix */
unsigned int rev = svr & 0xffff;
+ /* MPC8313 Rev < 2.0 */
+ if (pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020)
+ priv->errata |= GFAR_ERRATA_12;
+
/* MPC8313 Rev 2.0 and higher; All MPC837x */
if ((pvr == 0x80850010 && mod == 0x80b0 && rev >= 0x0020) ||
(pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
@@ -1984,7 +1988,8 @@ static inline struct txfcb *gfar_add_fcb
return fcb;
}
-static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb)
+static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb,
+ int has_csum_bug)
{
u8 flags = 0;
@@ -1994,6 +1999,17 @@ static inline void gfar_tx_checksum(stru
*/
flags = TXFCB_DEFAULT;
+ /* If using old-rev silicon, the alignment of the TXFCB may be off,
+ * causing TCP checksumming to fail (errata eTSEC12). In that case,
+ * we compute the checksum manually.
+ */
+ if (has_csum_bug) {
+ /* Disable handling of TCP/UDP header (checksumming) */
+ flags &= ~TXFCB_TUP;
+ /* Manually add checksum */
+ skb_checksum_help(skb);
+ }
+
/* Tell the controller what the protocol is */
/* And provide the already calculated phcs */
if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
@@ -2159,7 +2175,8 @@ static int gfar_start_xmit(struct sk_buf
if (CHECKSUM_PARTIAL == skb->ip_summed) {
fcb = gfar_add_fcb(skb);
lstatus |= BD_LFLAG(TXBD_TOE);
- gfar_tx_checksum(skb, fcb);
+ gfar_tx_checksum(skb, fcb,
+ gfar_has_errata(priv, GFAR_ERRATA_12));
}
if (priv->vlgrp && vlan_tx_tag_present(skb)) {
diff -purN orig/drivers/net/gianfar.h linux-2.6.36/drivers/net/gianfar.h
--- orig/drivers/net/gianfar.h 2010-11-03 15:10:29.257142194 -0400
+++ linux-2.6.36/drivers/net/gianfar.h 2010-11-03 15:48:10.117134959 -0400
@@ -1029,6 +1029,7 @@ enum gfar_errata {
GFAR_ERRATA_74 = 0x01,
GFAR_ERRATA_76 = 0x02,
GFAR_ERRATA_A002 = 0x04,
+ GFAR_ERRATA_12 = 0x08,
};
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
--
Matthew L. Creech
More information about the Linuxppc-dev
mailing list