aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-06-27 03:47:37 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-07-11 01:15:52 -0400
commitd212f87b068c9d72065ef579d85b5ee6b8b59381 (patch)
treea194d5c667c277c7ea5392c9cf97857a0cd1d321
parentd3d6dd3adaaad71eae20902ed81808a66a40a5b9 (diff)
[NET]: IPV6 checksum offloading in network devices
The existing model for checksum offload does not correctly handle devices that can offload IPV4 and IPV6 only. The NETIF_F_HW_CSUM flag implies device can do any arbitrary protocol. This patch: * adds NETIF_F_IPV6_CSUM for those devices * fixes bnx2 and tg3 devices that need it * add NETIF_F_IPV6_CSUM to ipv6 output (incl GSO) * fixes assumptions about NETIF_F_ALL_CSUM in nat * adjusts bridge union of checksumming computation Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/bnx2.c6
-rw-r--r--drivers/net/tg3.c7
-rw-r--r--include/linux/netdevice.h8
-rw-r--r--net/bridge/br_if.c10
-rw-r--r--net/core/dev.c24
-rw-r--r--net/ipv4/af_inet.c3
-rw-r--r--net/ipv4/ip_output.c2
-rw-r--r--net/ipv4/netfilter/nf_nat_helper.c4
-rw-r--r--net/ipv6/ipv6_sockglue.c2
9 files changed, 49 insertions, 17 deletions
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index ce3ed67a878e..0f4f76fda26f 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -6490,10 +6490,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
6490 memcpy(dev->perm_addr, bp->mac_addr, 6); 6490 memcpy(dev->perm_addr, bp->mac_addr, 6);
6491 bp->name = board_info[ent->driver_data].name; 6491 bp->name = board_info[ent->driver_data].name;
6492 6492
6493 dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
6493 if (CHIP_NUM(bp) == CHIP_NUM_5709) 6494 if (CHIP_NUM(bp) == CHIP_NUM_5709)
6494 dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; 6495 dev->features |= NETIF_F_IPV6_CSUM;
6495 else 6496
6496 dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
6497#ifdef BCM_VLAN 6497#ifdef BCM_VLAN
6498 dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; 6498 dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
6499#endif 6499#endif
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 2f3184184ad9..3a43426ced32 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -11944,12 +11944,11 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
11944 * checksumming. 11944 * checksumming.
11945 */ 11945 */
11946 if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) { 11946 if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) {
11947 dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
11947 if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || 11948 if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
11948 GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) 11949 GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
11949 dev->features |= NETIF_F_HW_CSUM; 11950 dev->features |= NETIF_F_IPV6_CSUM;
11950 else 11951
11951 dev->features |= NETIF_F_IP_CSUM;
11952 dev->features |= NETIF_F_SG;
11953 tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS; 11952 tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS;
11954 } else 11953 } else
11955 tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS; 11954 tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e7913ee5581c..7a8f22fb4eee 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -314,9 +314,10 @@ struct net_device
314 /* Net device features */ 314 /* Net device features */
315 unsigned long features; 315 unsigned long features;
316#define NETIF_F_SG 1 /* Scatter/gather IO. */ 316#define NETIF_F_SG 1 /* Scatter/gather IO. */
317#define NETIF_F_IP_CSUM 2 /* Can checksum only TCP/UDP over IPv4. */ 317#define NETIF_F_IP_CSUM 2 /* Can checksum TCP/UDP over IPv4. */
318#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */ 318#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */
319#define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */ 319#define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */
320#define NETIF_F_IPV6_CSUM 16 /* Can checksum TCP/UDP over IPV6 */
320#define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */ 321#define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */
321#define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */ 322#define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */
322#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */ 323#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
@@ -338,8 +339,11 @@ struct net_device
338 /* List of features with software fallbacks. */ 339 /* List of features with software fallbacks. */
339#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6) 340#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6)
340 341
342
341#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) 343#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
342#define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) 344#define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
345#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
346#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
343 347
344 struct net_device *next_sched; 348 struct net_device *next_sched;
345 349
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 849deaf14108..7b4ce9113be2 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -368,10 +368,18 @@ void br_features_recompute(struct net_bridge *br)
368 list_for_each_entry(p, &br->port_list, list) { 368 list_for_each_entry(p, &br->port_list, list) {
369 unsigned long feature = p->dev->features; 369 unsigned long feature = p->dev->features;
370 370
371 /* if device needs checksumming, downgrade to hw checksumming */
371 if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM)) 372 if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM))
372 checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM; 373 checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
374
375 /* if device can't do all checksum, downgrade to ipv4/ipv6 */
373 if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM)) 376 if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM))
374 checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM; 377 checksum ^= NETIF_F_HW_CSUM
378 | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
379
380 if (checksum & NETIF_F_IPV6_CSUM && !(feature & NETIF_F_IPV6_CSUM))
381 checksum &= ~NETIF_F_IPV6_CSUM;
382
375 if (!(feature & NETIF_F_IP_CSUM)) 383 if (!(feature & NETIF_F_IP_CSUM))
376 checksum = 0; 384 checksum = 0;
377 385
diff --git a/net/core/dev.c b/net/core/dev.c
index ee051bb398a0..a0a46e7ed137 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1509,9 +1509,11 @@ int dev_queue_xmit(struct sk_buff *skb)
1509 skb_set_transport_header(skb, skb->csum_start - 1509 skb_set_transport_header(skb, skb->csum_start -
1510 skb_headroom(skb)); 1510 skb_headroom(skb));
1511 1511
1512 if (!(dev->features & NETIF_F_GEN_CSUM) && 1512 if (!(dev->features & NETIF_F_GEN_CSUM)
1513 (!(dev->features & NETIF_F_IP_CSUM) || 1513 || ((dev->features & NETIF_F_IP_CSUM)
1514 skb->protocol != htons(ETH_P_IP))) 1514 && skb->protocol == htons(ETH_P_IP))
1515 || ((dev->features & NETIF_F_IPV6_CSUM)
1516 && skb->protocol == htons(ETH_P_IPV6)))
1515 if (skb_checksum_help(skb)) 1517 if (skb_checksum_help(skb))
1516 goto out_kfree_skb; 1518 goto out_kfree_skb;
1517 } 1519 }
@@ -3107,6 +3109,22 @@ int register_netdevice(struct net_device *dev)
3107 } 3109 }
3108 } 3110 }
3109 3111
3112 /* Fix illegal checksum combinations */
3113 if ((dev->features & NETIF_F_HW_CSUM) &&
3114 (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
3115 printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n",
3116 dev->name);
3117 dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
3118 }
3119
3120 if ((dev->features & NETIF_F_NO_CSUM) &&
3121 (dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
3122 printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n",
3123 dev->name);
3124 dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);
3125 }
3126
3127
3110 /* Fix illegal SG+CSUM combinations. */ 3128 /* Fix illegal SG+CSUM combinations. */
3111 if ((dev->features & NETIF_F_SG) && 3129 if ((dev->features & NETIF_F_SG) &&
3112 !(dev->features & NETIF_F_ALL_CSUM)) { 3130 !(dev->features & NETIF_F_ALL_CSUM)) {
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 041fba3fa0aa..06c08e5740fb 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1170,6 +1170,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
1170 int ihl; 1170 int ihl;
1171 int id; 1171 int id;
1172 1172
1173 if (!(features & NETIF_F_V4_CSUM))
1174 features &= ~NETIF_F_SG;
1175
1173 if (unlikely(skb_shinfo(skb)->gso_type & 1176 if (unlikely(skb_shinfo(skb)->gso_type &
1174 ~(SKB_GSO_TCPV4 | 1177 ~(SKB_GSO_TCPV4 |
1175 SKB_GSO_UDP | 1178 SKB_GSO_UDP |
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 34ea4547ebbe..a7dd343d3a03 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -837,7 +837,7 @@ int ip_append_data(struct sock *sk,
837 */ 837 */
838 if (transhdrlen && 838 if (transhdrlen &&
839 length + fragheaderlen <= mtu && 839 length + fragheaderlen <= mtu &&
840 rt->u.dst.dev->features & NETIF_F_ALL_CSUM && 840 rt->u.dst.dev->features & NETIF_F_V4_CSUM &&
841 !exthdrlen) 841 !exthdrlen)
842 csummode = CHECKSUM_PARTIAL; 842 csummode = CHECKSUM_PARTIAL;
843 843
diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c
index 15b6e5ce3a04..b1aa5983a95b 100644
--- a/net/ipv4/netfilter/nf_nat_helper.c
+++ b/net/ipv4/netfilter/nf_nat_helper.c
@@ -178,7 +178,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff **pskb,
178 datalen = (*pskb)->len - iph->ihl*4; 178 datalen = (*pskb)->len - iph->ihl*4;
179 if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { 179 if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
180 if (!(rt->rt_flags & RTCF_LOCAL) && 180 if (!(rt->rt_flags & RTCF_LOCAL) &&
181 (*pskb)->dev->features & NETIF_F_ALL_CSUM) { 181 (*pskb)->dev->features & NETIF_F_V4_CSUM) {
182 (*pskb)->ip_summed = CHECKSUM_PARTIAL; 182 (*pskb)->ip_summed = CHECKSUM_PARTIAL;
183 (*pskb)->csum_start = skb_headroom(*pskb) + 183 (*pskb)->csum_start = skb_headroom(*pskb) +
184 skb_network_offset(*pskb) + 184 skb_network_offset(*pskb) +
@@ -265,7 +265,7 @@ nf_nat_mangle_udp_packet(struct sk_buff **pskb,
265 265
266 if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { 266 if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
267 if (!(rt->rt_flags & RTCF_LOCAL) && 267 if (!(rt->rt_flags & RTCF_LOCAL) &&
268 (*pskb)->dev->features & NETIF_F_ALL_CSUM) { 268 (*pskb)->dev->features & NETIF_F_V4_CSUM) {
269 (*pskb)->ip_summed = CHECKSUM_PARTIAL; 269 (*pskb)->ip_summed = CHECKSUM_PARTIAL;
270 (*pskb)->csum_start = skb_headroom(*pskb) + 270 (*pskb)->csum_start = skb_headroom(*pskb) +
271 skb_network_offset(*pskb) + 271 skb_network_offset(*pskb) +
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b636c38411fb..1c3506696cb5 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -123,7 +123,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
123 struct ipv6hdr *ipv6h; 123 struct ipv6hdr *ipv6h;
124 struct inet6_protocol *ops; 124 struct inet6_protocol *ops;
125 125
126 if (!(features & NETIF_F_HW_CSUM)) 126 if (!(features & NETIF_F_V6_CSUM))
127 features &= ~NETIF_F_SG; 127 features &= ~NETIF_F_SG;
128 128
129 if (unlikely(skb_shinfo(skb)->gso_type & 129 if (unlikely(skb_shinfo(skb)->gso_type &