aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/hyperv/netvsc_drv.c
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 /drivers/net/hyperv/netvsc_drv.c
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>
Diffstat (limited to 'drivers/net/hyperv/netvsc_drv.c')
-rw-r--r--drivers/net/hyperv/netvsc_drv.c69
1 files changed, 67 insertions, 2 deletions
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);