diff options
author | KY Srinivasan <kys@microsoft.com> | 2014-04-10 00:00:47 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-04-11 21:15:12 +0200 |
commit | af9893a3dc790ae0c4d3e68adde12bc3cb9c63fa (patch) | |
tree | 7a95879de0f7b22181a67c2e7e23e7407d97b529 /drivers/net/hyperv/netvsc_drv.c | |
parent | Drivers: net: hyperv: Negotiate suitable ndis version for offload support (diff) | |
download | linux-af9893a3dc790ae0c4d3e68adde12bc3cb9c63fa.tar.xz linux-af9893a3dc790ae0c4d3e68adde12bc3cb9c63fa.zip |
Drivers: net: hyperv: Address UDP checksum issues
ws2008r2 does not support UDP checksum offload. Thus, we cannnot turn on
UDP offload in the host. Also, on ws2012 and ws2012 r2, there appear to be
an issue with UDP checksum offload.
Fix this issue by computing the UDP checksum in the Hyper-V driver.
Based on Dave Miller's comments, in this version, I have COWed the skb
before modifying the UDP header (the checksum field).
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/hyperv/netvsc_drv.c')
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 6f39baa67a5f..31e55fba7cad 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -398,7 +398,30 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) csum_info->transmit.tcp_checksum = 1; csum_info->transmit.tcp_header_offset = hdr_offset; } else if (net_trans_info & INFO_UDP) { - csum_info->transmit.udp_checksum = 1; + /* UDP checksum offload is not supported on ws2008r2. + * Furthermore, on ws2012 and ws2012r2, there are some + * issues with udp checksum offload from Linux guests. + * (these are host issues). + * For now compute the checksum here. + */ + struct udphdr *uh; + u16 udp_len; + + ret = skb_cow_head(skb, 0); + if (ret) + goto drop; + + uh = udp_hdr(skb); + udp_len = ntohs(uh->len); + uh->check = 0; + uh->check = csum_tcpudp_magic(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + udp_len, IPPROTO_UDP, + csum_partial(uh, udp_len, 0)); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + + csum_info->transmit.udp_checksum = 0; } goto do_send; @@ -438,6 +461,7 @@ do_send: ret = netvsc_send(net_device_ctx->device_ctx, packet); +drop: if (ret == 0) { net->stats.tx_bytes += skb->len; net->stats.tx_packets++; |