aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKY Srinivasan <kys@microsoft.com>2014-03-08 22:23:17 -0500
committerDavid S. Miller <davem@davemloft.net>2014-03-10 15:51:37 -0400
commit08cd04bf6d5b14ea90845b596d371bfa33eaba06 (patch)
treeb6b49bf2a1deb8aff42e560e733035be2c550c5b
parente3d605ed441cf4d113f9a1cf9e1b3f7cabe0d781 (diff)
Drivers: net: hyperv: Enable send side checksum offload
Enable send side checksum offload. 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>
-rw-r--r--drivers/net/hyperv/hyperv_net.h10
-rw-r--r--drivers/net/hyperv/netvsc_drv.c69
2 files changed, 77 insertions, 2 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index faeb74623fbd..4cf238234321 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -1035,6 +1035,16 @@ struct rndis_message {
1035#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 1035#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
1036#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 1036#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
1037 1037
1038#define INFO_IPV4 2
1039#define INFO_IPV6 4
1040#define INFO_TCP 2
1041#define INFO_UDP 4
1042
1043#define TRANSPORT_INFO_NOT_IP 0
1044#define TRANSPORT_INFO_IPV4_TCP ((INFO_IPV4 << 16) | INFO_TCP)
1045#define TRANSPORT_INFO_IPV4_UDP ((INFO_IPV4 << 16) | INFO_UDP)
1046#define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP)
1047#define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP)
1038 1048
1039 1049
1040#endif /* _HYPERV_NET_H */ 1050#endif /* _HYPERV_NET_H */
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 9bee0650c7ca..697837537ccb 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -257,6 +257,35 @@ static int netvsc_get_slots(struct sk_buff *skb)
257 return slots + frag_slots; 257 return slots + frag_slots;
258} 258}
259 259
260static u32 get_net_transport_info(struct sk_buff *skb, u32 *trans_off)
261{
262 u32 ret_val = TRANSPORT_INFO_NOT_IP;
263
264 if ((eth_hdr(skb)->h_proto != htons(ETH_P_IP)) &&
265 (eth_hdr(skb)->h_proto != htons(ETH_P_IPV6))) {
266 goto not_ip;
267 }
268
269 *trans_off = skb_transport_offset(skb);
270
271 if ((eth_hdr(skb)->h_proto == htons(ETH_P_IP))) {
272 struct iphdr *iphdr = ip_hdr(skb);
273
274 if (iphdr->protocol == IPPROTO_TCP)
275 ret_val = TRANSPORT_INFO_IPV4_TCP;
276 else if (iphdr->protocol == IPPROTO_UDP)
277 ret_val = TRANSPORT_INFO_IPV4_UDP;
278 } else {
279 if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
280 ret_val = TRANSPORT_INFO_IPV6_TCP;
281 else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
282 ret_val = TRANSPORT_INFO_IPV6_UDP;
283 }
284
285not_ip:
286 return ret_val;
287}
288
260static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) 289static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
261{ 290{
262 struct net_device_context *net_device_ctx = netdev_priv(net); 291 struct net_device_context *net_device_ctx = netdev_priv(net);
@@ -268,6 +297,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
268 u32 rndis_msg_size; 297 u32 rndis_msg_size;
269 bool isvlan; 298 bool isvlan;
270 struct rndis_per_packet_info *ppi; 299 struct rndis_per_packet_info *ppi;
300 struct ndis_tcp_ip_checksum_info *csum_info;
301 int hdr_offset;
302 u32 net_trans_info;
303
271 304
272 /* We will atmost need two pages to describe the rndis 305 /* We will atmost need two pages to describe the rndis
273 * header. We can only transmit MAX_PAGE_BUFFER_COUNT number 306 * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
@@ -335,6 +368,37 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
335 VLAN_PRIO_SHIFT; 368 VLAN_PRIO_SHIFT;
336 } 369 }
337 370
371 net_trans_info = get_net_transport_info(skb, &hdr_offset);
372 if (net_trans_info == TRANSPORT_INFO_NOT_IP)
373 goto do_send;
374
375 /*
376 * Setup the sendside checksum offload only if this is not a
377 * GSO packet.
378 */
379 if (skb_is_gso(skb))
380 goto do_send;
381
382 rndis_msg_size += NDIS_CSUM_PPI_SIZE;
383 ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
384 TCPIP_CHKSUM_PKTINFO);
385
386 csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
387 ppi->ppi_offset);
388
389 if (net_trans_info & (INFO_IPV4 << 16))
390 csum_info->transmit.is_ipv4 = 1;
391 else
392 csum_info->transmit.is_ipv6 = 1;
393
394 if (net_trans_info & INFO_TCP) {
395 csum_info->transmit.tcp_checksum = 1;
396 csum_info->transmit.tcp_header_offset = hdr_offset;
397 } else if (net_trans_info & INFO_UDP) {
398 csum_info->transmit.udp_checksum = 1;
399 }
400
401do_send:
338 /* Start filling in the page buffers with the rndis hdr */ 402 /* Start filling in the page buffers with the rndis hdr */
339 rndis_msg->msg_len += rndis_msg_size; 403 rndis_msg->msg_len += rndis_msg_size;
340 packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, 404 packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
@@ -589,8 +653,9 @@ static int netvsc_probe(struct hv_device *dev,
589 net->netdev_ops = &device_ops; 653 net->netdev_ops = &device_ops;
590 654
591 /* TODO: Add GSO and Checksum offload */ 655 /* TODO: Add GSO and Checksum offload */
592 net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG; 656 net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM;
593 net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM; 657 net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM |
658 NETIF_F_IP_CSUM;
594 659
595 SET_ETHTOOL_OPS(net, &ethtool_ops); 660 SET_ETHTOOL_OPS(net, &ethtool_ops);
596 SET_NETDEV_DEV(net, &dev->device); 661 SET_NETDEV_DEV(net, &dev->device);