Skip to content

Commit

Permalink
[NET]: IPV6 checksum offloading in network devices
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Stephen Hemminger authored and David S. Miller committed Jul 11, 2007
1 parent d3d6dd3 commit d212f87
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 17 deletions.
6 changes: 3 additions & 3 deletions drivers/net/bnx2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
bp->name = board_info[ent->driver_data].name;

dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
if (CHIP_NUM(bp) == CHIP_NUM_5709)
dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG;
else
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
dev->features |= NETIF_F_IPV6_CSUM;

#ifdef BCM_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
#endif
Expand Down
7 changes: 3 additions & 4 deletions drivers/net/tg3.c
Original file line number Diff line number Diff line change
Expand Up @@ -11944,12 +11944,11 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
* checksumming.
*/
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 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
dev->features |= NETIF_F_HW_CSUM;
else
dev->features |= NETIF_F_IP_CSUM;
dev->features |= NETIF_F_SG;
dev->features |= NETIF_F_IPV6_CSUM;

tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS;
} else
tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS;
Expand Down
8 changes: 6 additions & 2 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,10 @@ struct net_device
/* Net device features */
unsigned long features;
#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_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_FRAGLIST 64 /* Scatter/gather IO. */
#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
Expand All @@ -338,8 +339,11 @@ struct net_device
/* List of features with software fallbacks. */
#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_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;

Expand Down
10 changes: 9 additions & 1 deletion net/bridge/br_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,18 @@ void br_features_recompute(struct net_bridge *br)
list_for_each_entry(p, &br->port_list, list) {
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))
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))
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))
checksum = 0;

Expand Down
24 changes: 21 additions & 3 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1509,9 +1509,11 @@ int dev_queue_xmit(struct sk_buff *skb)
skb_set_transport_header(skb, skb->csum_start -
skb_headroom(skb));

if (!(dev->features & NETIF_F_GEN_CSUM) &&
(!(dev->features & NETIF_F_IP_CSUM) ||
skb->protocol != htons(ETH_P_IP)))
if (!(dev->features & NETIF_F_GEN_CSUM)
|| ((dev->features & NETIF_F_IP_CSUM)
&& skb->protocol == htons(ETH_P_IP))
|| ((dev->features & NETIF_F_IPV6_CSUM)
&& skb->protocol == htons(ETH_P_IPV6)))
if (skb_checksum_help(skb))
goto out_kfree_skb;
}
Expand Down Expand Up @@ -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. */
if ((dev->features & NETIF_F_SG) &&
!(dev->features & NETIF_F_ALL_CSUM)) {
Expand Down
3 changes: 3 additions & 0 deletions net/ipv4/af_inet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
int ihl;
int id;

if (!(features & NETIF_F_V4_CSUM))
features &= ~NETIF_F_SG;

if (unlikely(skb_shinfo(skb)->gso_type &
~(SKB_GSO_TCPV4 |
SKB_GSO_UDP |
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ int ip_append_data(struct sock *sk,
*/
if (transhdrlen &&
length + fragheaderlen <= mtu &&
rt->u.dst.dev->features & NETIF_F_ALL_CSUM &&
rt->u.dst.dev->features & NETIF_F_V4_CSUM &&
!exthdrlen)
csummode = CHECKSUM_PARTIAL;

Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/netfilter/nf_nat_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ nf_nat_mangle_tcp_packet(struct sk_buff **pskb,
datalen = (*pskb)->len - iph->ihl*4;
if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
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)->csum_start = skb_headroom(*pskb) +
skb_network_offset(*pskb) +
Expand Down Expand Up @@ -265,7 +265,7 @@ nf_nat_mangle_udp_packet(struct sk_buff **pskb,

if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
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)->csum_start = skb_headroom(*pskb) +
skb_network_offset(*pskb) +
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/ipv6_sockglue.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
struct ipv6hdr *ipv6h;
struct inet6_protocol *ops;

if (!(features & NETIF_F_HW_CSUM))
if (!(features & NETIF_F_V6_CSUM))
features &= ~NETIF_F_SG;

if (unlikely(skb_shinfo(skb)->gso_type &
Expand Down

0 comments on commit d212f87

Please sign in to comment.