Skip to content

Commit

Permalink
Drivers: net: hyperv: Address UDP checksum issues
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Haiyang Zhang <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
kattisrinivasan authored and davem330 committed Apr 11, 2014
1 parent 1f73db4 commit af9893a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 2 deletions.
1 change: 1 addition & 0 deletions drivers/net/hyperv/hyperv_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ struct ndis_oject_header {
#define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0
#define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1

#define VERSION_4_OFFLOAD_SIZE 22
/*
* New offload OIDs for NDIS 6
*/
Expand Down
26 changes: 25 additions & 1 deletion drivers/net/hyperv/netvsc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -438,6 +461,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)

ret = netvsc_send(net_device_ctx->device_ctx, packet);

drop:
if (ret == 0) {
net->stats.tx_bytes += skb->len;
net->stats.tx_packets++;
Expand Down
12 changes: 11 additions & 1 deletion drivers/net/hyperv/rndis_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,16 @@ int rndis_filter_set_offload_params(struct hv_device *hdev,
struct rndis_set_complete *set_complete;
u32 extlen = sizeof(struct ndis_offload_params);
int ret, t;
u32 vsp_version = nvdev->nvsp_version;

if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
extlen = VERSION_4_OFFLOAD_SIZE;
/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
* UDP checksum offload.
*/
req_offloads->udp_ip_v4_csum = 0;
req_offloads->udp_ip_v6_csum = 0;
}

request = get_rndis_request(rdev, RNDIS_MSG_SET,
RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
Expand Down Expand Up @@ -674,7 +684,7 @@ int rndis_filter_set_offload_params(struct hv_device *hdev,
} else {
set_complete = &request->response_msg.msg.set_complete;
if (set_complete->status != RNDIS_STATUS_SUCCESS) {
netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
set_complete->status);
ret = -EINVAL;
}
Expand Down

0 comments on commit af9893a

Please sign in to comment.