diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-06-27 03:47:37 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-11 01:15:52 -0400 |
commit | d212f87b068c9d72065ef579d85b5ee6b8b59381 (patch) | |
tree | a194d5c667c277c7ea5392c9cf97857a0cd1d321 | |
parent | d3d6dd3adaaad71eae20902ed81808a66a40a5b9 (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.c | 6 | ||||
-rw-r--r-- | drivers/net/tg3.c | 7 | ||||
-rw-r--r-- | include/linux/netdevice.h | 8 | ||||
-rw-r--r-- | net/bridge/br_if.c | 10 | ||||
-rw-r--r-- | net/core/dev.c | 24 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 3 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_helper.c | 4 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 2 |
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 & |