]>
Commit | Line | Data |
---|---|---|
a7b0967d MT |
1 | From b822ee7a17efd1bc8c7584da5d0a2c042e9ca5b6 Mon Sep 17 00:00:00 2001 |
2 | From: KY Srinivasan <kys@microsoft.com> | |
3 | Date: Wed, 9 Apr 2014 15:00:47 -0700 | |
4 | Subject: [PATCH 14/25] Drivers: net: hyperv: Address UDP checksum issues | |
5 | ||
6 | ws2008r2 does not support UDP checksum offload. Thus, we cannnot turn on | |
7 | UDP offload in the host. Also, on ws2012 and ws2012 r2, there appear to be | |
8 | an issue with UDP checksum offload. | |
9 | Fix this issue by computing the UDP checksum in the Hyper-V driver. | |
10 | ||
11 | Based on Dave Miller's comments, in this version, I have COWed the skb | |
12 | before modifying the UDP header (the checksum field). | |
13 | ||
14 | Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> | |
15 | Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> | |
16 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
17 | --- | |
18 | drivers/net/hyperv/hyperv_net.h | 1 + | |
19 | drivers/net/hyperv/netvsc_drv.c | 26 +++++++++++++++++++++++++- | |
20 | drivers/net/hyperv/rndis_filter.c | 12 +++++++++++- | |
21 | 3 files changed, 37 insertions(+), 2 deletions(-) | |
22 | ||
23 | diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h | |
24 | index 13010b4dae5b..d18f711d0b0c 100644 | |
25 | --- a/drivers/net/hyperv/hyperv_net.h | |
26 | +++ b/drivers/net/hyperv/hyperv_net.h | |
27 | @@ -747,6 +747,7 @@ struct ndis_oject_header { | |
28 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0 | |
29 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1 | |
30 | ||
31 | +#define VERSION_4_OFFLOAD_SIZE 22 | |
32 | /* | |
33 | * New offload OIDs for NDIS 6 | |
34 | */ | |
35 | diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c | |
36 | index c76c85176644..0d898876689e 100644 | |
37 | --- a/drivers/net/hyperv/netvsc_drv.c | |
38 | +++ b/drivers/net/hyperv/netvsc_drv.c | |
39 | @@ -399,7 +399,30 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |
40 | csum_info->transmit.tcp_checksum = 1; | |
41 | csum_info->transmit.tcp_header_offset = hdr_offset; | |
42 | } else if (net_trans_info & INFO_UDP) { | |
43 | - csum_info->transmit.udp_checksum = 1; | |
44 | + /* UDP checksum offload is not supported on ws2008r2. | |
45 | + * Furthermore, on ws2012 and ws2012r2, there are some | |
46 | + * issues with udp checksum offload from Linux guests. | |
47 | + * (these are host issues). | |
48 | + * For now compute the checksum here. | |
49 | + */ | |
50 | + struct udphdr *uh; | |
51 | + u16 udp_len; | |
52 | + | |
53 | + ret = skb_cow_head(skb, 0); | |
54 | + if (ret) | |
55 | + goto drop; | |
56 | + | |
57 | + uh = udp_hdr(skb); | |
58 | + udp_len = ntohs(uh->len); | |
59 | + uh->check = 0; | |
60 | + uh->check = csum_tcpudp_magic(ip_hdr(skb)->saddr, | |
61 | + ip_hdr(skb)->daddr, | |
62 | + udp_len, IPPROTO_UDP, | |
63 | + csum_partial(uh, udp_len, 0)); | |
64 | + if (uh->check == 0) | |
65 | + uh->check = CSUM_MANGLED_0; | |
66 | + | |
67 | + csum_info->transmit.udp_checksum = 0; | |
68 | } | |
69 | goto do_send; | |
70 | ||
71 | @@ -439,6 +462,7 @@ do_send: | |
72 | ||
73 | ret = netvsc_send(net_device_ctx->device_ctx, packet); | |
74 | ||
75 | +drop: | |
76 | if (ret == 0) { | |
77 | net->stats.tx_bytes += skb_length; | |
78 | net->stats.tx_packets++; | |
79 | diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c | |
80 | index 4a37e3db9e32..143a98caf618 100644 | |
81 | --- a/drivers/net/hyperv/rndis_filter.c | |
82 | +++ b/drivers/net/hyperv/rndis_filter.c | |
83 | @@ -641,6 +641,16 @@ int rndis_filter_set_offload_params(struct hv_device *hdev, | |
84 | struct rndis_set_complete *set_complete; | |
85 | u32 extlen = sizeof(struct ndis_offload_params); | |
86 | int ret, t; | |
87 | + u32 vsp_version = nvdev->nvsp_version; | |
88 | + | |
89 | + if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { | |
90 | + extlen = VERSION_4_OFFLOAD_SIZE; | |
91 | + /* On NVSP_PROTOCOL_VERSION_4 and below, we do not support | |
92 | + * UDP checksum offload. | |
93 | + */ | |
94 | + req_offloads->udp_ip_v4_csum = 0; | |
95 | + req_offloads->udp_ip_v6_csum = 0; | |
96 | + } | |
97 | ||
98 | request = get_rndis_request(rdev, RNDIS_MSG_SET, | |
99 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); | |
100 | @@ -674,7 +684,7 @@ int rndis_filter_set_offload_params(struct hv_device *hdev, | |
101 | } else { | |
102 | set_complete = &request->response_msg.msg.set_complete; | |
103 | if (set_complete->status != RNDIS_STATUS_SUCCESS) { | |
104 | - netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", | |
105 | + netdev_err(ndev, "Fail to set offload on host side:0x%x\n", | |
106 | set_complete->status); | |
107 | ret = -EINVAL; | |
108 | } | |
109 | -- | |
110 | 2.4.3 | |
111 |