Commit d212f87b authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

[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: default avatarDavid S. Miller <davem@davemloft.net>
parent d3d6dd3a
...@@ -6490,10 +6490,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -6490,10 +6490,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
memcpy(dev->perm_addr, bp->mac_addr, 6); memcpy(dev->perm_addr, bp->mac_addr, 6);
bp->name = board_info[ent->driver_data].name; bp->name = board_info[ent->driver_data].name;
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
if (CHIP_NUM(bp) == CHIP_NUM_5709) if (CHIP_NUM(bp) == CHIP_NUM_5709)
dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; dev->features |= NETIF_F_IPV6_CSUM;
else
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
#ifdef BCM_VLAN #ifdef BCM_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
#endif #endif
......
...@@ -11944,12 +11944,11 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, ...@@ -11944,12 +11944,11 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
* checksumming. * checksumming.
*/ */
if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) { if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) {
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
dev->features |= NETIF_F_HW_CSUM; dev->features |= NETIF_F_IPV6_CSUM;
else
dev->features |= NETIF_F_IP_CSUM;
dev->features |= NETIF_F_SG;
tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS; tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS;
} else } else
tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS; tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS;
......
...@@ -314,9 +314,10 @@ struct net_device ...@@ -314,9 +314,10 @@ struct net_device
/* Net device features */ /* Net device features */
unsigned long features; unsigned long features;
#define NETIF_F_SG 1 /* Scatter/gather IO. */ #define NETIF_F_SG 1 /* Scatter/gather IO. */
#define NETIF_F_IP_CSUM 2 /* Can checksum only TCP/UDP over IPv4. */ #define NETIF_F_IP_CSUM 2 /* Can checksum TCP/UDP over IPv4. */
#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */ #define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */
#define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */ #define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */
#define NETIF_F_IPV6_CSUM 16 /* Can checksum TCP/UDP over IPV6 */
#define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */ #define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */
#define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */ #define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */
#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */ #define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
...@@ -338,8 +339,11 @@ struct net_device ...@@ -338,8 +339,11 @@ struct net_device
/* List of features with software fallbacks. */ /* List of features with software fallbacks. */
#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6) #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6)
#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
#define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) #define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
struct net_device *next_sched; struct net_device *next_sched;
......
...@@ -368,10 +368,18 @@ void br_features_recompute(struct net_bridge *br) ...@@ -368,10 +368,18 @@ void br_features_recompute(struct net_bridge *br)
list_for_each_entry(p, &br->port_list, list) { list_for_each_entry(p, &br->port_list, list) {
unsigned long feature = p->dev->features; unsigned long feature = p->dev->features;
/* if device needs checksumming, downgrade to hw checksumming */
if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM)) if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM))
checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM; checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
/* if device can't do all checksum, downgrade to ipv4/ipv6 */
if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM)) if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM))
checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM; checksum ^= NETIF_F_HW_CSUM
| NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
if (checksum & NETIF_F_IPV6_CSUM && !(feature & NETIF_F_IPV6_CSUM))
checksum &= ~NETIF_F_IPV6_CSUM;
if (!(feature & NETIF_F_IP_CSUM)) if (!(feature & NETIF_F_IP_CSUM))
checksum = 0; checksum = 0;
......
...@@ -1509,9 +1509,11 @@ int dev_queue_xmit(struct sk_buff *skb) ...@@ -1509,9 +1509,11 @@ int dev_queue_xmit(struct sk_buff *skb)
skb_set_transport_header(skb, skb->csum_start - skb_set_transport_header(skb, skb->csum_start -
skb_headroom(skb)); skb_headroom(skb));
if (!(dev->features & NETIF_F_GEN_CSUM) && if (!(dev->features & NETIF_F_GEN_CSUM)
(!(dev->features & NETIF_F_IP_CSUM) || || ((dev->features & NETIF_F_IP_CSUM)
skb->protocol != htons(ETH_P_IP))) && skb->protocol == htons(ETH_P_IP))
|| ((dev->features & NETIF_F_IPV6_CSUM)
&& skb->protocol == htons(ETH_P_IPV6)))
if (skb_checksum_help(skb)) if (skb_checksum_help(skb))
goto out_kfree_skb; goto out_kfree_skb;
} }
...@@ -3107,6 +3109,22 @@ int register_netdevice(struct net_device *dev) ...@@ -3107,6 +3109,22 @@ int register_netdevice(struct net_device *dev)
} }
} }
/* Fix illegal checksum combinations */
if ((dev->features & NETIF_F_HW_CSUM) &&
(dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n",
dev->name);
dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
}
if ((dev->features & NETIF_F_NO_CSUM) &&
(dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n",
dev->name);
dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);
}
/* Fix illegal SG+CSUM combinations. */ /* Fix illegal SG+CSUM combinations. */
if ((dev->features & NETIF_F_SG) && if ((dev->features & NETIF_F_SG) &&
!(dev->features & NETIF_F_ALL_CSUM)) { !(dev->features & NETIF_F_ALL_CSUM)) {
......
...@@ -1170,6 +1170,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) ...@@ -1170,6 +1170,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
int ihl; int ihl;
int id; int id;
if (!(features & NETIF_F_V4_CSUM))
features &= ~NETIF_F_SG;
if (unlikely(skb_shinfo(skb)->gso_type & if (unlikely(skb_shinfo(skb)->gso_type &
~(SKB_GSO_TCPV4 | ~(SKB_GSO_TCPV4 |
SKB_GSO_UDP | SKB_GSO_UDP |
......
...@@ -837,7 +837,7 @@ int ip_append_data(struct sock *sk, ...@@ -837,7 +837,7 @@ int ip_append_data(struct sock *sk,
*/ */
if (transhdrlen && if (transhdrlen &&
length + fragheaderlen <= mtu && length + fragheaderlen <= mtu &&
rt->u.dst.dev->features & NETIF_F_ALL_CSUM && rt->u.dst.dev->features & NETIF_F_V4_CSUM &&
!exthdrlen) !exthdrlen)
csummode = CHECKSUM_PARTIAL; csummode = CHECKSUM_PARTIAL;
......
...@@ -178,7 +178,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff **pskb, ...@@ -178,7 +178,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff **pskb,
datalen = (*pskb)->len - iph->ihl*4; datalen = (*pskb)->len - iph->ihl*4;
if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
if (!(rt->rt_flags & RTCF_LOCAL) && if (!(rt->rt_flags & RTCF_LOCAL) &&
(*pskb)->dev->features & NETIF_F_ALL_CSUM) { (*pskb)->dev->features & NETIF_F_V4_CSUM) {
(*pskb)->ip_summed = CHECKSUM_PARTIAL; (*pskb)->ip_summed = CHECKSUM_PARTIAL;
(*pskb)->csum_start = skb_headroom(*pskb) + (*pskb)->csum_start = skb_headroom(*pskb) +
skb_network_offset(*pskb) + skb_network_offset(*pskb) +
...@@ -265,7 +265,7 @@ nf_nat_mangle_udp_packet(struct sk_buff **pskb, ...@@ -265,7 +265,7 @@ nf_nat_mangle_udp_packet(struct sk_buff **pskb,
if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
if (!(rt->rt_flags & RTCF_LOCAL) && if (!(rt->rt_flags & RTCF_LOCAL) &&
(*pskb)->dev->features & NETIF_F_ALL_CSUM) { (*pskb)->dev->features & NETIF_F_V4_CSUM) {
(*pskb)->ip_summed = CHECKSUM_PARTIAL; (*pskb)->ip_summed = CHECKSUM_PARTIAL;
(*pskb)->csum_start = skb_headroom(*pskb) + (*pskb)->csum_start = skb_headroom(*pskb) +
skb_network_offset(*pskb) + skb_network_offset(*pskb) +
......
...@@ -123,7 +123,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) ...@@ -123,7 +123,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
struct ipv6hdr *ipv6h; struct ipv6hdr *ipv6h;
struct inet6_protocol *ops; struct inet6_protocol *ops;
if (!(features & NETIF_F_HW_CSUM)) if (!(features & NETIF_F_V6_CSUM))
features &= ~NETIF_F_SG; features &= ~NETIF_F_SG;
if (unlikely(skb_shinfo(skb)->gso_type & if (unlikely(skb_shinfo(skb)->gso_type &
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment