aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhayeswang <hayeswang@realtek.com>2014-07-11 04:25:58 -0400
committerDavid S. Miller <davem@davemloft.net>2014-07-11 17:32:54 -0400
commite974604b453e87f8d864371786375d3d511fdf56 (patch)
tree8a0b41a5cc8db77b424ee30a8efbb472c08d4fb0
parentbdfa4ed68187c436caee8adc1ef1d4b0d75ca0ae (diff)
r8169: support IPv6
Support the IPv6 hw checksum for RTL8111C and later chips. Note that the hw has the limitation for the transport offset. The checksum must be calculated by sw, when the transport offset is out of the range which the hw accepts. Signed-off-by: Hayes Wang <hayeswang@realtek.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/realtek/r8169.c157
1 files changed, 145 insertions, 12 deletions
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index e5a0bdbfbb2b..51c78ce27b37 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -27,6 +27,8 @@
27#include <linux/firmware.h> 27#include <linux/firmware.h>
28#include <linux/pci-aspm.h> 28#include <linux/pci-aspm.h>
29#include <linux/prefetch.h> 29#include <linux/prefetch.h>
30#include <linux/ipv6.h>
31#include <net/ip6_checksum.h>
30 32
31#include <asm/io.h> 33#include <asm/io.h>
32#include <asm/irq.h> 34#include <asm/irq.h>
@@ -628,11 +630,16 @@ enum rtl_tx_desc_bit_0 {
628enum rtl_tx_desc_bit_1 { 630enum rtl_tx_desc_bit_1 {
629 /* First doubleword. */ 631 /* First doubleword. */
630 TD1_GTSENV4 = (1 << 26), /* Giant Send for IPv4 */ 632 TD1_GTSENV4 = (1 << 26), /* Giant Send for IPv4 */
633 TD1_GTSENV6 = (1 << 25), /* Giant Send for IPv6 */
631#define GTTCPHO_SHIFT 18 634#define GTTCPHO_SHIFT 18
635#define GTTCPHO_MAX 0x7fU
632 636
633 /* Second doubleword. */ 637 /* Second doubleword. */
638#define TCPHO_SHIFT 18
639#define TCPHO_MAX 0x3ffU
634#define TD1_MSS_SHIFT 18 /* MSS position (11 bits) */ 640#define TD1_MSS_SHIFT 18 /* MSS position (11 bits) */
635 TD1_IP_CS = (1 << 29), /* Calculate IP checksum */ 641 TD1_IPv6_CS = (1 << 28), /* Calculate IPv6 checksum */
642 TD1_IPv4_CS = (1 << 29), /* Calculate IPv4 checksum */
636 TD1_TCP_CS = (1 << 30), /* Calculate TCP/IP checksum */ 643 TD1_TCP_CS = (1 << 30), /* Calculate TCP/IP checksum */
637 TD1_UDP_CS = (1 << 31), /* Calculate UDP/IP checksum */ 644 TD1_UDP_CS = (1 << 31), /* Calculate UDP/IP checksum */
638}; 645};
@@ -5920,6 +5927,82 @@ static bool rtl_test_hw_pad_bug(struct rtl8169_private *tp, struct sk_buff *skb)
5920 return skb->len < ETH_ZLEN && tp->mac_version == RTL_GIGA_MAC_VER_34; 5927 return skb->len < ETH_ZLEN && tp->mac_version == RTL_GIGA_MAC_VER_34;
5921} 5928}
5922 5929
5930static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
5931 struct net_device *dev);
5932/* r8169_csum_workaround()
5933 * The hw limites the value the transport offset. When the offset is out of the
5934 * range, calculate the checksum by sw.
5935 */
5936static void r8169_csum_workaround(struct rtl8169_private *tp,
5937 struct sk_buff *skb)
5938{
5939 if (skb_shinfo(skb)->gso_size) {
5940 netdev_features_t features = tp->dev->features;
5941 struct sk_buff *segs, *nskb;
5942
5943 features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
5944 segs = skb_gso_segment(skb, features);
5945 if (IS_ERR(segs) || !segs)
5946 goto drop;
5947
5948 do {
5949 nskb = segs;
5950 segs = segs->next;
5951 nskb->next = NULL;
5952 rtl8169_start_xmit(nskb, tp->dev);
5953 } while (segs);
5954
5955 dev_kfree_skb(skb);
5956 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
5957 if (skb_checksum_help(skb) < 0)
5958 goto drop;
5959
5960 rtl8169_start_xmit(skb, tp->dev);
5961 } else {
5962 struct net_device_stats *stats;
5963
5964drop:
5965 stats = &tp->dev->stats;
5966 stats->tx_dropped++;
5967 dev_kfree_skb(skb);
5968 }
5969}
5970
5971/* msdn_giant_send_check()
5972 * According to the document of microsoft, the TCP Pseudo Header excludes the
5973 * packet length for IPv6 TCP large packets.
5974 */
5975static int msdn_giant_send_check(struct sk_buff *skb)
5976{
5977 const struct ipv6hdr *ipv6h;
5978 struct tcphdr *th;
5979 int ret;
5980
5981 ret = skb_cow_head(skb, 0);
5982 if (ret)
5983 return ret;
5984
5985 ipv6h = ipv6_hdr(skb);
5986 th = tcp_hdr(skb);
5987
5988 th->check = 0;
5989 th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0);
5990
5991 return ret;
5992}
5993
5994static inline __be16 get_protocol(struct sk_buff *skb)
5995{
5996 __be16 protocol;
5997
5998 if (skb->protocol == htons(ETH_P_8021Q))
5999 protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
6000 else
6001 protocol = skb->protocol;
6002
6003 return protocol;
6004}
6005
5923static bool rtl8169_tso_csum_v1(struct rtl8169_private *tp, 6006static bool rtl8169_tso_csum_v1(struct rtl8169_private *tp,
5924 struct sk_buff *skb, u32 *opts) 6007 struct sk_buff *skb, u32 *opts)
5925{ 6008{
@@ -5949,21 +6032,69 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp,
5949 u32 mss = skb_shinfo(skb)->gso_size; 6032 u32 mss = skb_shinfo(skb)->gso_size;
5950 6033
5951 if (mss) { 6034 if (mss) {
5952 opts[0] |= TD1_GTSENV4; 6035 if (transport_offset > GTTCPHO_MAX) {
6036 netif_warn(tp, tx_err, tp->dev,
6037 "Invalid transport offset 0x%x for TSO\n",
6038 transport_offset);
6039 return false;
6040 }
6041
6042 switch (get_protocol(skb)) {
6043 case htons(ETH_P_IP):
6044 opts[0] |= TD1_GTSENV4;
6045 break;
6046
6047 case htons(ETH_P_IPV6):
6048 if (msdn_giant_send_check(skb))
6049 return false;
6050
6051 opts[0] |= TD1_GTSENV6;
6052 break;
6053
6054 default:
6055 WARN_ON_ONCE(1);
6056 break;
6057 }
6058
5953 opts[0] |= transport_offset << GTTCPHO_SHIFT; 6059 opts[0] |= transport_offset << GTTCPHO_SHIFT;
5954 opts[1] |= min(mss, TD_MSS_MAX) << TD1_MSS_SHIFT; 6060 opts[1] |= min(mss, TD_MSS_MAX) << TD1_MSS_SHIFT;
5955 } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 6061 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
5956 const struct iphdr *ip = ip_hdr(skb); 6062 u8 ip_protocol;
5957 6063
5958 if (unlikely(rtl_test_hw_pad_bug(tp, skb))) 6064 if (unlikely(rtl_test_hw_pad_bug(tp, skb)))
5959 return skb_checksum_help(skb) == 0 && rtl_skb_pad(skb); 6065 return skb_checksum_help(skb) == 0 && rtl_skb_pad(skb);
5960 6066
5961 if (ip->protocol == IPPROTO_TCP) 6067 if (transport_offset > TCPHO_MAX) {
5962 opts[1] |= TD1_IP_CS | TD1_TCP_CS; 6068 netif_warn(tp, tx_err, tp->dev,
5963 else if (ip->protocol == IPPROTO_UDP) 6069 "Invalid transport offset 0x%x\n",
5964 opts[1] |= TD1_IP_CS | TD1_UDP_CS; 6070 transport_offset);
6071 return false;
6072 }
6073
6074 switch (get_protocol(skb)) {
6075 case htons(ETH_P_IP):
6076 opts[1] |= TD1_IPv4_CS;
6077 ip_protocol = ip_hdr(skb)->protocol;
6078 break;
6079
6080 case htons(ETH_P_IPV6):
6081 opts[1] |= TD1_IPv6_CS;
6082 ip_protocol = ipv6_hdr(skb)->nexthdr;
6083 break;
6084
6085 default:
6086 ip_protocol = IPPROTO_RAW;
6087 break;
6088 }
6089
6090 if (ip_protocol == IPPROTO_TCP)
6091 opts[1] |= TD1_TCP_CS;
6092 else if (ip_protocol == IPPROTO_UDP)
6093 opts[1] |= TD1_UDP_CS;
5965 else 6094 else
5966 WARN_ON_ONCE(1); 6095 WARN_ON_ONCE(1);
6096
6097 opts[1] |= transport_offset << TCPHO_SHIFT;
5967 } else { 6098 } else {
5968 if (unlikely(rtl_test_hw_pad_bug(tp, skb))) 6099 if (unlikely(rtl_test_hw_pad_bug(tp, skb)))
5969 return rtl_skb_pad(skb); 6100 return rtl_skb_pad(skb);
@@ -5996,8 +6127,10 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
5996 opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb)); 6127 opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb));
5997 opts[0] = DescOwn; 6128 opts[0] = DescOwn;
5998 6129
5999 if (!tp->tso_csum(tp, skb, opts)) 6130 if (!tp->tso_csum(tp, skb, opts)) {
6000 goto err_update_stats; 6131 r8169_csum_workaround(tp, skb);
6132 return NETDEV_TX_OK;
6133 }
6001 6134
6002 len = skb_headlen(skb); 6135 len = skb_headlen(skb);
6003 mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE); 6136 mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE);
@@ -6062,7 +6195,6 @@ err_dma_1:
6062 rtl8169_unmap_tx_skb(d, tp->tx_skb + entry, txd); 6195 rtl8169_unmap_tx_skb(d, tp->tx_skb + entry, txd);
6063err_dma_0: 6196err_dma_0:
6064 dev_kfree_skb_any(skb); 6197 dev_kfree_skb_any(skb);
6065err_update_stats:
6066 dev->stats.tx_dropped++; 6198 dev->stats.tx_dropped++;
6067 return NETDEV_TX_OK; 6199 return NETDEV_TX_OK;
6068 6200
@@ -7149,9 +7281,10 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
7149 7281
7150 if (tp->txd_version == RTL_TD_0) 7282 if (tp->txd_version == RTL_TD_0)
7151 tp->tso_csum = rtl8169_tso_csum_v1; 7283 tp->tso_csum = rtl8169_tso_csum_v1;
7152 else if (tp->txd_version == RTL_TD_1) 7284 else if (tp->txd_version == RTL_TD_1) {
7153 tp->tso_csum = rtl8169_tso_csum_v2; 7285 tp->tso_csum = rtl8169_tso_csum_v2;
7154 else 7286 dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
7287 } else
7155 WARN_ON_ONCE(1); 7288 WARN_ON_ONCE(1);
7156 7289
7157 dev->hw_features |= NETIF_F_RXALL; 7290 dev->hw_features |= NETIF_F_RXALL;