Drivers: net: hyperv: Enable large send offload
authorKY Srinivasan <kys@microsoft.com>
Sun, 9 Mar 2014 03:23:18 +0000 (19:23 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 10 Mar 2014 19:51:37 +0000 (15:51 -0400)
Enable segmentation 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>
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc_drv.c

index 4cf238234321996c92b04e3e0b297396dbf9c848..7d06b49593839f36684df444ae796ea25586c28c 100644 (file)
@@ -742,6 +742,10 @@ struct ndis_oject_header {
 #define NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED 3
 #define NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED 4
 
+#define NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE    1
+#define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4       0
+#define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6       1
+
 /*
  * New offload OIDs for NDIS 6
  */
@@ -804,12 +808,48 @@ struct ndis_tcp_ip_checksum_info {
        };
 };
 
+struct ndis_tcp_lso_info {
+       union {
+               struct {
+                       u32 unused:30;
+                       u32 type:1;
+                       u32 reserved2:1;
+               } transmit;
+               struct {
+                       u32 mss:20;
+                       u32 tcp_header_offset:10;
+                       u32 type:1;
+                       u32 reserved2:1;
+               } lso_v1_transmit;
+               struct {
+                       u32 tcp_payload:30;
+                       u32 type:1;
+                       u32 reserved2:1;
+               } lso_v1_transmit_complete;
+               struct {
+                       u32 mss:20;
+                       u32 tcp_header_offset:10;
+                       u32 type:1;
+                       u32 ip_version:1;
+               } lso_v2_transmit;
+               struct {
+                       u32 reserved:30;
+                       u32 type:1;
+                       u32 reserved2:1;
+               } lso_v2_transmit_complete;
+               u32  value;
+       };
+};
+
 #define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
                sizeof(struct ndis_pkt_8021q_info))
 
 #define NDIS_CSUM_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
                sizeof(struct ndis_tcp_ip_checksum_info))
 
+#define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
+               sizeof(struct ndis_tcp_lso_info))
+
 /* Format of Information buffer passed in a SetRequest for the OID */
 /* OID_GEN_RNDIS_CONFIG_PARAMETER. */
 struct rndis_config_parameter_info {
index 697837537ccbf6540c03b22f84e54adedf12b355..3d069901e6d951ed6e1e9817a505b34fd3c021b6 100644 (file)
@@ -298,6 +298,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        bool isvlan;
        struct rndis_per_packet_info *ppi;
        struct ndis_tcp_ip_checksum_info *csum_info;
+       struct ndis_tcp_lso_info *lso_info;
        int  hdr_offset;
        u32 net_trans_info;
 
@@ -377,7 +378,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
         * GSO packet.
         */
        if (skb_is_gso(skb))
-               goto do_send;
+               goto do_lso;
 
        rndis_msg_size += NDIS_CSUM_PPI_SIZE;
        ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
@@ -397,6 +398,35 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        } else if (net_trans_info & INFO_UDP) {
                csum_info->transmit.udp_checksum = 1;
        }
+       goto do_send;
+
+do_lso:
+       rndis_msg_size += NDIS_LSO_PPI_SIZE;
+       ppi = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE,
+                           TCP_LARGESEND_PKTINFO);
+
+       lso_info = (struct ndis_tcp_lso_info *)((void *)ppi +
+                       ppi->ppi_offset);
+
+       lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE;
+       if (net_trans_info & (INFO_IPV4 << 16)) {
+               lso_info->lso_v2_transmit.ip_version =
+                       NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4;
+               ip_hdr(skb)->tot_len = 0;
+               ip_hdr(skb)->check = 0;
+               tcp_hdr(skb)->check =
+               ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+                                  ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
+       } else {
+               lso_info->lso_v2_transmit.ip_version =
+                       NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6;
+               ipv6_hdr(skb)->payload_len = 0;
+               tcp_hdr(skb)->check =
+               ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+                               &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
+       }
+       lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset;
+       lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size;
 
 do_send:
        /* Start filling in the page buffers with the rndis hdr */
@@ -652,10 +682,10 @@ static int netvsc_probe(struct hv_device *dev,
 
        net->netdev_ops = &device_ops;
 
-       /* TODO: Add GSO and Checksum offload */
-       net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM;
+       net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM |
+                               NETIF_F_TSO;
        net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM |
-                       NETIF_F_IP_CSUM;
+                       NETIF_F_IP_CSUM | NETIF_F_TSO;
 
        SET_ETHTOOL_OPS(net, &ethtool_ops);
        SET_NETDEV_DEV(net, &dev->device);