diff options
| author | Michael Chan <mchan@broadcom.com> | 2006-06-29 23:15:54 -0400 |
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-30 17:11:57 -0400 |
| commit | 52c0fd834ea0e7c6ef8616ce0a1f85bac4233ed7 (patch) | |
| tree | 92897bedf97906699f654fd94985fbada0497daa /drivers | |
| parent | 1661394e78b3b2cc868cd0e89c1066974302aaca (diff) | |
[TG3]: Add TSO workaround using GSO
Use GSO to workaround a rare TSO bug on some chips. This hardware
bug may be triggered when the TSO header size is greater than 80
bytes. When this condition is detected in a TSO packet, the driver
will use GSO to segment the packet to workaround the hardware bug.
Thanks to Juergen Kreileder <jk@blackdown.de> for reporting the
problem and collecting traces to help debug the problem.
And thanks to Herbert Xu <herbert@gondor.apana.org.au> for providing
the GSO mechanism that happens to be the perfect workaround for this
problem.
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/tg3.c | 53 | ||||
| -rw-r--r-- | drivers/net/tg3.h | 3 |
2 files changed, 51 insertions, 5 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 2c36e70e37e5..63d4b2c797e8 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c | |||
| @@ -3880,6 +3880,40 @@ out_unlock: | |||
| 3880 | return NETDEV_TX_OK; | 3880 | return NETDEV_TX_OK; |
| 3881 | } | 3881 | } |
| 3882 | 3882 | ||
| 3883 | #if TG3_TSO_SUPPORT != 0 | ||
| 3884 | static int tg3_start_xmit_dma_bug(struct sk_buff *, struct net_device *); | ||
| 3885 | |||
| 3886 | /* Use GSO to workaround a rare TSO bug that may be triggered when the | ||
| 3887 | * TSO header is greater than 80 bytes. | ||
| 3888 | */ | ||
| 3889 | static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb) | ||
| 3890 | { | ||
| 3891 | struct sk_buff *segs, *nskb; | ||
| 3892 | |||
| 3893 | /* Estimate the number of fragments in the worst case */ | ||
| 3894 | if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->gso_segs * 3))) { | ||
| 3895 | netif_stop_queue(tp->dev); | ||
| 3896 | return NETDEV_TX_BUSY; | ||
| 3897 | } | ||
| 3898 | |||
| 3899 | segs = skb_gso_segment(skb, tp->dev->features & ~NETIF_F_TSO); | ||
| 3900 | if (unlikely(IS_ERR(segs))) | ||
| 3901 | goto tg3_tso_bug_end; | ||
| 3902 | |||
| 3903 | do { | ||
| 3904 | nskb = segs; | ||
| 3905 | segs = segs->next; | ||
| 3906 | nskb->next = NULL; | ||
| 3907 | tg3_start_xmit_dma_bug(nskb, tp->dev); | ||
| 3908 | } while (segs); | ||
| 3909 | |||
| 3910 | tg3_tso_bug_end: | ||
| 3911 | dev_kfree_skb(skb); | ||
| 3912 | |||
| 3913 | return NETDEV_TX_OK; | ||
| 3914 | } | ||
| 3915 | #endif | ||
| 3916 | |||
| 3883 | /* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and | 3917 | /* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and |
| 3884 | * support TG3_FLG2_HW_TSO_1 or firmware TSO only. | 3918 | * support TG3_FLG2_HW_TSO_1 or firmware TSO only. |
| 3885 | */ | 3919 | */ |
| @@ -3916,7 +3950,7 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev) | |||
| 3916 | mss = 0; | 3950 | mss = 0; |
| 3917 | if (skb->len > (tp->dev->mtu + ETH_HLEN) && | 3951 | if (skb->len > (tp->dev->mtu + ETH_HLEN) && |
| 3918 | (mss = skb_shinfo(skb)->gso_size) != 0) { | 3952 | (mss = skb_shinfo(skb)->gso_size) != 0) { |
| 3919 | int tcp_opt_len, ip_tcp_len; | 3953 | int tcp_opt_len, ip_tcp_len, hdr_len; |
| 3920 | 3954 | ||
| 3921 | if (skb_header_cloned(skb) && | 3955 | if (skb_header_cloned(skb) && |
| 3922 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { | 3956 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { |
| @@ -3927,11 +3961,16 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev) | |||
| 3927 | tcp_opt_len = ((skb->h.th->doff - 5) * 4); | 3961 | tcp_opt_len = ((skb->h.th->doff - 5) * 4); |
| 3928 | ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); | 3962 | ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); |
| 3929 | 3963 | ||
| 3964 | hdr_len = ip_tcp_len + tcp_opt_len; | ||
| 3965 | if (unlikely((ETH_HLEN + hdr_len) > 80) && | ||
| 3966 | (tp->tg3_flags2 & TG3_FLG2_HW_TSO_1_BUG)) | ||
| 3967 | return (tg3_tso_bug(tp, skb)); | ||
| 3968 | |||
| 3930 | base_flags |= (TXD_FLAG_CPU_PRE_DMA | | 3969 | base_flags |= (TXD_FLAG_CPU_PRE_DMA | |
| 3931 | TXD_FLAG_CPU_POST_DMA); | 3970 | TXD_FLAG_CPU_POST_DMA); |
| 3932 | 3971 | ||
| 3933 | skb->nh.iph->check = 0; | 3972 | skb->nh.iph->check = 0; |
| 3934 | skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); | 3973 | skb->nh.iph->tot_len = htons(mss + hdr_len); |
| 3935 | if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { | 3974 | if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { |
| 3936 | skb->h.th->check = 0; | 3975 | skb->h.th->check = 0; |
| 3937 | base_flags &= ~TXD_FLAG_TCPUDP_CSUM; | 3976 | base_flags &= ~TXD_FLAG_TCPUDP_CSUM; |
| @@ -10192,8 +10231,14 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) | |||
| 10192 | GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { | 10231 | GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { |
| 10193 | tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; | 10232 | tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; |
| 10194 | tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; | 10233 | tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; |
| 10195 | } else | 10234 | } else { |
| 10196 | tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1; | 10235 | tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | |
| 10236 | TG3_FLG2_HW_TSO_1_BUG; | ||
| 10237 | if (GET_ASIC_REV(tp->pci_chip_rev_id) == | ||
| 10238 | ASIC_REV_5750 && | ||
| 10239 | tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2) | ||
| 10240 | tp->tg3_flags2 &= ~TG3_FLG2_HW_TSO_1_BUG; | ||
| 10241 | } | ||
| 10197 | } | 10242 | } |
| 10198 | 10243 | ||
| 10199 | if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && | 10244 | if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && |
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 97a860433b50..ba2c98711c88 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h | |||
| @@ -125,6 +125,7 @@ | |||
| 125 | #define CHIPREV_ID_5750_A0 0x4000 | 125 | #define CHIPREV_ID_5750_A0 0x4000 |
| 126 | #define CHIPREV_ID_5750_A1 0x4001 | 126 | #define CHIPREV_ID_5750_A1 0x4001 |
| 127 | #define CHIPREV_ID_5750_A3 0x4003 | 127 | #define CHIPREV_ID_5750_A3 0x4003 |
| 128 | #define CHIPREV_ID_5750_C2 0x4202 | ||
| 128 | #define CHIPREV_ID_5752_A0_HW 0x5000 | 129 | #define CHIPREV_ID_5752_A0_HW 0x5000 |
| 129 | #define CHIPREV_ID_5752_A0 0x6000 | 130 | #define CHIPREV_ID_5752_A0 0x6000 |
| 130 | #define CHIPREV_ID_5752_A1 0x6001 | 131 | #define CHIPREV_ID_5752_A1 0x6001 |
| @@ -2193,7 +2194,7 @@ struct tg3 { | |||
| 2193 | #define TG3_FLAG_INIT_COMPLETE 0x80000000 | 2194 | #define TG3_FLAG_INIT_COMPLETE 0x80000000 |
| 2194 | u32 tg3_flags2; | 2195 | u32 tg3_flags2; |
| 2195 | #define TG3_FLG2_RESTART_TIMER 0x00000001 | 2196 | #define TG3_FLG2_RESTART_TIMER 0x00000001 |
| 2196 | /* 0x00000002 available */ | 2197 | #define TG3_FLG2_HW_TSO_1_BUG 0x00000002 |
| 2197 | #define TG3_FLG2_NO_ETH_WIRE_SPEED 0x00000004 | 2198 | #define TG3_FLG2_NO_ETH_WIRE_SPEED 0x00000004 |
| 2198 | #define TG3_FLG2_IS_5788 0x00000008 | 2199 | #define TG3_FLG2_IS_5788 0x00000008 |
| 2199 | #define TG3_FLG2_MAX_RXPEND_64 0x00000010 | 2200 | #define TG3_FLG2_MAX_RXPEND_64 0x00000010 |
