Drivers: net: hyperv: Enable various offloads
Subsystems (3)
v2 cover Drivers: net: hyperv: Enable various offloads
This patch set enables both checksum as well as segmentation offload. As part of this effort I have enabled scatter gather I/O a well.
In this version of the patches, I have addressed comments from David Miller and Dan Carpenter.
K. Y. Srinivasan (6):
Drivers: net: hyperv: Enable scatter gather I/O
Drivers: net: hyperv: Cleanup the send path
Drivers: net: hyperv: Enable offloads on the host
Drivers: net: hyperv: Enable receive side IP checksum offload
Drivers: net: hyperv: Enable send side checksum offload
Drivers: net: hyperv: Enable large send offload
drivers/net/hyperv/hyperv_net.h | 135 ++++++++++++++- drivers/net/hyperv/netvsc_drv.c | 336 +++++++++++++++++++++++++++++++------ drivers/net/hyperv/rndis_filter.c | 153 ++++++++++-------- 3 files changed, 501 insertions(+), 123 deletions(-)
Signature · 1 line
1.7.4.1
PATCH v2 1/6 Drivers: net: hyperv: Enable scatter gather I/O
Cleanup the code and enable scatter gather I/O.
--- drivers/net/hyperv/netvsc_drv.c | 153 +++++++++++++++++++++++++++++---------- 1 files changed, 114 insertions(+), 39 deletions(-)
drivers/net/hyperv/netvsc_drv.c +114 -39
@@ -140,21 +140,123 @@ static void netvsc_xmit_completion(void *context)dev_kfree_skb_any (skb); } +static u32 fill_pg_buf (struct page *page,u32 offset,u32 len, +struct hv_page_buffer *pb) +{ +int j =0 ; + +/* Deal with compund pages by ignoring unused part +* of the page. +*/ + page += offset >>PAGE_SHIFT ; + offset &= ~PAGE_MASK ; + +while (len >0 ) { +unsigned long bytes; + + bytes =PAGE_SIZE - offset; +if (bytes > len) + bytes = len; + pb[j].pfn =page_to_pfn (page); + pb[j].offset = offset; + pb[j].len = bytes; + + offset += bytes; + len -= bytes; + +if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + } + } + +return j +1 ; +} + +static void init_page_array (void *hdr,u32 len,struct sk_buff *skb, +struct hv_page_buffer *pb) +{ +u32 slots_used =0 ; +char *data = skb->data; +int frags =skb_shinfo (skb)->nr_frags; +int i; + +/* The packet is laid out thus: +* 1. hdr +* 2. skb linear data +* 3. skb fragment data +*/ +if (hdr !=NULL ) + slots_used +=fill_pg_buf (virt_to_page (hdr), +offset_in_page (hdr), + len, &pb[slots_used]); + + slots_used +=fill_pg_buf (virt_to_page (data), +offset_in_page (data), +skb_headlen (skb), &pb[slots_used]); + +for (i =0 ; i < frags; i++) { +skb_frag_t *frag =skb_shinfo (skb)->frags + i; + + slots_used +=fill_pg_buf (skb_frag_page (frag), + frag->page_offset, +skb_frag_size (frag), &pb[slots_used]); + } +} + +static int count_skb_frag_slots (struct sk_buff *skb) +{ +int i, frags =skb_shinfo (skb)->nr_frags; +int pages =0 ; + +for (i =0 ; i < frags; i++) { +skb_frag_t *frag =skb_shinfo (skb)->frags + i; +unsigned long size =skb_frag_size (frag); +unsigned long offset = frag->page_offset; + +/* Skip unused frames from start of page */ + offset &= ~PAGE_MASK ; + pages +=PFN_UP (offset + size); + } +return pages; +} + +static int netvsc_get_slots (struct sk_buff *skb) +{ +char *data = skb->data; +unsigned int offset =offset_in_page (data); +unsigned int len =skb_headlen (skb); +int slots; +int frag_slots; + + slots =DIV_ROUND_UP (offset + len,PAGE_SIZE ); + frag_slots =count_skb_frag_slots (skb); +return slots + frag_slots; +} +static int netvsc_start_xmit (struct sk_buff *skb,struct net_device *net) {struct net_device_context *net_device_ctx =netdev_priv (net);struct hv_netvsc_packet *packet;int ret; -unsigned int i, num_pages, npg_data; +unsigned int num_data_pages; -/* Add multipages for skb->data and additional 2 for RNDIS */ - npg_data = (((unsigned long )skb->data +skb_headlen (skb) -1 ) - >>PAGE_SHIFT ) - ((unsigned long )skb->data >>PAGE_SHIFT ) +1 ; - num_pages =skb_shinfo (skb)->nr_frags + npg_data +2 ; +/* We will atmost need two pages to describe the rndis +* header. We can only transmit MAX_PAGE_BUFFER_COUNT number +* of pages in a single packet. +*/ + num_data_pages =netvsc_get_slots (skb) +2 ; +if (num_data_pages >MAX_PAGE_BUFFER_COUNT ) { +netdev_err (net,"Packet too big: %u\n" , skb->len); +dev_kfree_skb (skb); + net->stats.tx_dropped++; +return NETDEV_TX_OK ; + }/* Allocate a netvsc packet based on # of frags. */ packet =kzalloc (sizeof (struct hv_netvsc_packet ) + - (num_pages *sizeof (struct hv_page_buffer )) + + (num_data_pages *sizeof (struct hv_page_buffer )) +sizeof (struct rndis_message ) +NDIS_VLAN_PPI_SIZE ,GFP_ATOMIC );if (!packet) {
@@ -169,44 +271,17 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) packet->vlan_tci = skb->vlan_tci; packet->extension = (void *)(unsigned long )packet + -sizeof (struct hv_netvsc_packet ) + - (num_pages *sizeof (struct hv_page_buffer )); +sizeof (struct hv_netvsc_packet ) + + (num_data_pages *sizeof (struct hv_page_buffer ));/* If the rndis msg goes beyond 1 page, we will add 1 later */ - packet->page_buf_cnt = num_pages -1 ; + packet->page_buf_cnt = num_data_pages -1 ;/* Initialize it from the skb */ packet->total_data_buflen = skb->len;/* Start filling in the page buffers starting after RNDIS buffer. */ - packet->page_buf[1 ].pfn =virt_to_phys (skb->data) >>PAGE_SHIFT ; - packet->page_buf[1 ].offset - = (unsigned long )skb->data & (PAGE_SIZE -1 ); -if (npg_data ==1 ) - packet->page_buf[1 ].len =skb_headlen (skb); -else - packet->page_buf[1 ].len =PAGE_SIZE - - packet->page_buf[1 ].offset; - -for (i =2 ; i <= npg_data; i++) { - packet->page_buf[i].pfn =virt_to_phys (skb->data - +PAGE_SIZE * (i-1 )) >>PAGE_SHIFT ; - packet->page_buf[i].offset =0 ; - packet->page_buf[i].len =PAGE_SIZE ; - } -if (npg_data >1 ) - packet->page_buf[npg_data].len = (((unsigned long )skb->data - +skb_headlen (skb) -1 ) & (PAGE_SIZE -1 )) +1 ; - -/* Additional fragments are after SKB data */ -for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { -const skb_frag_t *f = &skb_shinfo (skb)->frags[i]; - - packet->page_buf[i+npg_data+1 ].pfn = -page_to_pfn (skb_frag_page (f)); - packet->page_buf[i+npg_data+1 ].offset = f->page_offset; - packet->page_buf[i+npg_data+1 ].len =skb_frag_size (f); - } +init_page_array (NULL ,0 , skb, &packet->page_buf[1 ]);/* Set the completion routine */ packet->completion.send.send_completion = netvsc_xmit_completion;
@@ -451,8 +526,8 @@ static int netvsc_probe(struct hv_device *dev, net->netdev_ops = &device_ops;/* TODO: Add GSO and Checksum offload */ - net->hw_features =0 ; - net->features =NETIF_F_HW_VLAN_CTAG_TX ; + net->hw_features =NETIF_F_SG ; + net->features =NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_SG ;SET_ETHTOOL_OPS (net, ðtool_ops);SET_NETDEV_DEV (net, &dev->device);
Signature · 1 line
1.7.4.1
PATCH v2 2/6 Drivers: net: hyperv: Cleanup the send path
In preparation for enabling offloads, cleanup the send path.
--- drivers/net/hyperv/hyperv_net.h | 7 +--- drivers/net/hyperv/netvsc_drv.c | 88 +++++++++++++++++++++++++++++-------- drivers/net/hyperv/rndis_filter.c | 66 --------------------------- 3 files changed, 71 insertions(+), 90 deletions(-)
drivers/net/hyperv/hyperv_net.h +1 -6
@@ -73,7 +73,7 @@ struct hv_netvsc_packet { } completion;/* This points to the memory after page_buf */ -void *extension; +struct rndis_message *rndis_msg;u32 total_data_buflen;/* Points to the send/receive buffer where the ethernet frame is */
@@ -126,11 +126,6 @@ void rndis_filter_device_remove(struct hv_device *dev);int rndis_filter_receive (struct hv_device *dev,struct hv_netvsc_packet *pkt); - - -int rndis_filter_send (struct hv_device *dev, -struct hv_netvsc_packet *pkt); -int rndis_filter_set_packet_filter (struct rndis_device *dev,u32 new_filter);int rndis_filter_set_device_mac (struct hv_device *hdev,char *mac);
drivers/net/hyperv/netvsc_drv.c +70 -18
137 lines not on this page · raw message
drivers/net/hyperv/rndis_filter.c +0 -66
69 lines not on this page · raw message
Signature · 1 line
1.7.4.1
PATCH v3 3/6 Drivers: net: hyperv: Enable offloads on the host
In reply to K. Y. SrinivasanPrior to enabling guest side offloads, enable the offloads on the host.
--- drivers/net/hyperv/hyperv_net.h | 55 +++++++++++++++++++++++++ drivers/net/hyperv/rndis_filter.c | 80 +++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 0 deletions(-)
drivers/net/hyperv/hyperv_net.h +55 -0
@@ -721,6 +721,61 @@ struct ndis_pkt_8021q_info { }; }; +struct ndis_oject_header { +u8 type; +u8 revision; +u16 size; +}; + +#define NDIS_OBJECT_TYPE_DEFAULT 0x80 +#define NDIS_OFFLOAD_PARAMETERS_REVISION_3 3 +#define NDIS_OFFLOAD_PARAMETERS_NO_CHANGE 0 +#define NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED 1 +#define NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED 2 +#define NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED 2 +#define NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED 1 +#define NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED 2 +#define NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED 1 +#define NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED 2 +#define NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED 3 +#define NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED 4 + +/* +* New offload OIDs for NDIS 6 +*/ +#define OID_TCP_OFFLOAD_CURRENT_CONFIG 0xFC01020B/* query only */ +#define OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C/* set only */ +#define OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020D/* query only */ +#define OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG 0xFC01020E/* query only */ +#define OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020F/* query */ +#define OID_OFFLOAD_ENCAPSULATION 0x0101010A/* set/query */ + +struct ndis_offload_params { +struct ndis_oject_header header; +u8 ip_v4_csum; +u8 tcp_ip_v4_csum; +u8 udp_ip_v4_csum; +u8 tcp_ip_v6_csum; +u8 udp_ip_v6_csum; +u8 lso_v1; +u8 ip_sec_v1; +u8 lso_v2_ipv4; +u8 lso_v2_ipv6; +u8 tcp_connection_ip_v4; +u8 tcp_connection_ip_v6; +u32 flags; +u8 ip_sec_v2; +u8 ip_sec_v2_ip_v4; +struct { +u8 rsc_ip_v4; +u8 rsc_ip_v6; + }; +struct { +u8 encapsulated_packet_task_offload; +u8 encapsulation_types; + }; +}; +#define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ sizeof(struct ndis_pkt_8021q_info))
drivers/net/hyperv/rndis_filter.c +80 -0
104 lines not on this page · raw message
Signature · 1 line
1.7.4.1
PATCH v2 4/6 Drivers: net: hyperv: Enable receive side IP checksum offload
In reply to K. Y. SrinivasanEnable receive side checksum offload.
--- drivers/net/hyperv/hyperv_net.h | 33 ++++++++++++++++++++++++++++++++- drivers/net/hyperv/netvsc_drv.c | 19 +++++++++++++++---- drivers/net/hyperv/rndis_filter.c | 4 +++- 3 files changed, 50 insertions(+), 6 deletions(-)
drivers/net/hyperv/hyperv_net.h +32 -1
@@ -30,6 +30,7 @@/* Fwd declaration */ struct hv_netvsc_packet ; +struct ndis_tcp_ip_checksum_info ;/* Represent the xfer page packet which contains 1 or more netvsc packet */ struct xferpage_packet {
@@ -117,7 +118,8 @@ int netvsc_send(struct hv_device *device,void netvsc_linkstatus_callback (struct hv_device *device_obj,unsigned int status);int netvsc_recv_callback (struct hv_device *device_obj, -struct hv_netvsc_packet *packet); +struct hv_netvsc_packet *packet, +struct ndis_tcp_ip_checksum_info *csum_info);int rndis_filter_open (struct hv_device *dev);int rndis_filter_close (struct hv_device *dev); int rndis_filter_device_add(struct hv_device *dev,
@@ -776,9 +778,38 @@ struct ndis_offload_params { }; }; +struct ndis_tcp_ip_checksum_info { +union { +struct { +u32 is_ipv4:1 ; +u32 is_ipv6:1 ; +u32 tcp_checksum:1 ; +u32 udp_checksum:1 ; +u32 ip_header_checksum:1 ; +u32 reserved:11 ; +u32 tcp_header_offset:10 ; + } transmit; +struct { +u32 tcp_checksum_failed:1 ; +u32 udp_checksum_failed:1 ; +u32 ip_checksum_failed:1 ; +u32 tcp_checksum_succeeded:1 ; +u32 udp_checksum_succeeded:1 ; +u32 ip_checksum_succeeded:1 ; +u32 loopback:1 ; +u32 tcp_checksum_value_invalid:1 ; +u32 ip_checksum_value_invalid:1 ; + } receive; +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)) +/* Format of Information buffer passed in a SetRequest for the OID */ /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ struct rndis_config_parameter_info {
drivers/net/hyperv/netvsc_drv.c +15 -4
@@ -391,7 +391,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,* "wire" on the specified device. */ int netvsc_recv_callback (struct hv_device *device_obj, -struct hv_netvsc_packet *packet) +struct hv_netvsc_packet *packet, +struct ndis_tcp_ip_checksum_info *csum_info) {struct net_device *net;struct sk_buff *skb;
@@ -418,7 +419,17 @@ int netvsc_recv_callback(struct hv_device *device_obj, packet->total_data_buflen); skb->protocol =eth_type_trans (skb, net); - skb->ip_summed =CHECKSUM_NONE ; +if (csum_info) { +/* We only look at the IP checksum here. +* Should we be dropping the packet if checksum +* failed? How do we deal with other checksums - TCP/UDP? +*/ +if (csum_info->receive.ip_checksum_succeeded) + skb->ip_summed =CHECKSUM_UNNECESSARY ; +else + skb->ip_summed =CHECKSUM_NONE ; + } +if (packet->vlan_tci &VLAN_TAG_PRESENT )__vlan_hwaccel_put_tag (skb,htons (ETH_P_8021Q ), packet->vlan_tci);
@@ -578,8 +589,8 @@ static int netvsc_probe(struct hv_device *dev, net->netdev_ops = &device_ops;/* TODO: Add GSO and Checksum offload */ - net->hw_features =NETIF_F_SG ; - net->features =NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_SG ; + net->hw_features =NETIF_F_RXCSUM |NETIF_F_SG ; + net->features =NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_SG |NETIF_F_RXCSUM ;SET_ETHTOOL_OPS (net, ðtool_ops);SET_NETDEV_DEV (net, &dev->device);
drivers/net/hyperv/rndis_filter.c +3 -1
@@ -370,6 +370,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev,struct rndis_packet *rndis_pkt;u32 data_offset;struct ndis_pkt_8021q_info *vlan; +struct ndis_tcp_ip_checksum_info *csum_info; rndis_pkt = &msg->msg.pkt;
@@ -408,7 +409,8 @@ static void rndis_filter_receive_data(struct rndis_device *dev, pkt->vlan_tci =0 ; } -netvsc_recv_callback (dev->net_dev->dev, pkt); + csum_info =rndis_get_ppi (rndis_pkt,TCPIP_CHKSUM_PKTINFO ); +netvsc_recv_callback (dev->net_dev->dev, pkt, csum_info); } int rndis_filter_receive(struct hv_device *dev,
Signature · 1 line
1.7.4.1
PATCH v2 5/6 Drivers: net: hyperv: Enable send side checksum offload
In reply to K. Y. SrinivasanEnable send side checksum offload.
--- drivers/net/hyperv/hyperv_net.h | 10 ++++++ drivers/net/hyperv/netvsc_drv.c | 69 +++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 2 deletions(-)
drivers/net/hyperv/hyperv_net.h +10 -0
@@ -1035,6 +1035,16 @@ struct rndis_message {#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 +#define INFO_IPV4 2 +#define INFO_IPV6 4 +#define INFO_TCP 2 +#define INFO_UDP 4 + +#define TRANSPORT_INFO_NOT_IP 0 +#define TRANSPORT_INFO_IPV4_TCP ((INFO_IPV4 << 16) | INFO_TCP) +#define TRANSPORT_INFO_IPV4_UDP ((INFO_IPV4 << 16) | INFO_UDP) +#define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP) +#define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP)#endif /* _HYPERV_NET_H */
drivers/net/hyperv/netvsc_drv.c +67 -2
93 lines not on this page · raw message
Signature · 1 line
1.7.4.1
PATCH v2 6/6 Drivers: net: hyperv: Enable large send offload
In reply to K. Y. SrinivasanEnable segmentation offload.
--- drivers/net/hyperv/hyperv_net.h | 40 +++++++++++++++++++++++++++++++++++++++ drivers/net/hyperv/netvsc_drv.c | 38 +++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 4 deletions(-)
drivers/net/hyperv/hyperv_net.h +40 -0
@@ -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 {
drivers/net/hyperv/netvsc_drv.c +34 -4
@@ -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, ðtool_ops);SET_NETDEV_DEV (net, &dev->device);
Signature · 1 line
1.7.4.1
David Miller
maintainerRe: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/O
In reply to K. Y. SrinivasanFrom: "K. Y. Srinivasan" <[email protected]>
Date: Thu, 6 Mar 2014 21:32:36 -0800
drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 151
Quoted 9 lines
drivers/net/hyperv/netvsc_drv.c +9
-0+static u32 fill_pg_buf (struct page *page,u32 offset,u32 len, +struct hv_page_buffer *pb) +{ +int j =0 ; + +/* Deal with compund pages by ignoring unused part +* of the page. +*/ + page += offset >>PAGE_SHIFT ;
Please only one space between "offset" and ">>"
Quoted 14 lines
drivers/net/hyperv/netvsc_drv.c +14
-0+ offset &= ~PAGE_MASK ; + +while (len >0 ) { +unsigned long bytes; + + bytes =PAGE_SIZE - offset; +if (bytes > len) + bytes = len; + pb[j].pfn =page_to_pfn (page); + pb[j].offset = offset; + pb[j].len = bytes; + + offset += bytes; + len -= bytes;
drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 175
Quoted 10 lines
drivers/net/hyperv/netvsc_drv.c +10
-0+ +if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + } + } + +return j +1 ; +}
I think this function has some edge case errors.
As I understand it, this function returns how many page buffer entries were filled in.
But if we fill exactly the end of a page, we will report one too many in the return value.
Consider an "offset" of X, where X is smaller than PAGE_SIZE. And a "len" which is exactly "PAGE_SIZE - offset".
We will fill in exactly one page buffer entry, however we will erroneously return 2 from this function.
I believe the fix is to first replace the test at the end of the loop with:
page++; offset = 0; j++;
And subsequently change the function to just return plain "j".
RE: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/O
Quoted 53 lines
-----Original Message-----
From: David Miller [mailto:[email protected]]
Sent: Saturday, March 8, 2014 3:18 AM
To: KY Srinivasan
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/OFrom: "K. Y. Srinivasan" <[email protected]>
Date: Thu, 6 Mar 2014 21:32:36 -0800drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 151
Quoted 9 lines
drivers/net/hyperv/netvsc_drv.c +9
-0+static u32 fill_pg_buf (struct page *page,u32 offset,u32 len, +struct hv_page_buffer *pb) +{ +int j =0 ; + +/* Deal with compund pages by ignoring unused part +* of the page. +*/ + page += offset >>PAGE_SHIFT ;Please only one space between "offset" and ">>"
Quoted 14 lines
drivers/net/hyperv/netvsc_drv.c +14
-0+ offset &= ~PAGE_MASK ; + +while (len >0 ) { +unsigned long bytes; + + bytes =PAGE_SIZE - offset; +if (bytes > len) + bytes = len; + pb[j].pfn =page_to_pfn (page); + pb[j].offset = offset; + pb[j].len = bytes; + + offset += bytes; + len -= bytes;drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 175
Quoted 10 lines
drivers/net/hyperv/netvsc_drv.c +10
-0+ +if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + } + } + +return j +1 ; +}I think this function has some edge case errors.
Quoted 5 lines
As I understand it, this function returns how many page buffer entries were filled in.
But if we fill exactly the end of a page, we will report one too many in the return value.
I may be missing something here; but in the case you are describing we would return a value of one as we should. When offset + len is exactly equal to the page size, we would not increment the value of j, since at that point len == 0. Since the initial value of j was 0 and we return (j + 1), we will return 1.
Regards,
K. Y
Quoted 15 lines
Consider an "offset" of X, where X is smaller than
PAGE_SIZE. And a "len" which is exactly "PAGE_SIZE- offset".We will fill in exactly one page buffer entry, however we will erroneously return 2 from this function.
I believe the fix is to first replace the test at the end of the loop with:
page++; offset = 0; j++;And subsequently change the function to just return plain "j".
David Miller
maintainerRe: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/O
From: KY Srinivasan <[email protected]>
Date: Sat, 8 Mar 2014 04:12:01 +0000
Quoted 56 lines
Quoted 56 lines
-----Original Message-----
From: David Miller [mailto:[email protected]]
Sent: Saturday, March 8, 2014 3:18 AM
To: KY Srinivasan
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/OFrom: "K. Y. Srinivasan" <[email protected]>
Date: Thu, 6 Mar 2014 21:32:36 -0800drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 151
Quoted 9 lines
drivers/net/hyperv/netvsc_drv.c +9
-0+static u32 fill_pg_buf (struct page *page,u32 offset,u32 len, +struct hv_page_buffer *pb) +{ +int j =0 ; + +/* Deal with compund pages by ignoring unused part +* of the page. +*/ + page += offset >>PAGE_SHIFT ;Please only one space between "offset" and ">>"
Quoted 14 lines
drivers/net/hyperv/netvsc_drv.c +14
-0+ offset &= ~PAGE_MASK ; + +while (len >0 ) { +unsigned long bytes; + + bytes =PAGE_SIZE - offset; +if (bytes > len) + bytes = len; + pb[j].pfn =page_to_pfn (page); + pb[j].offset = offset; + pb[j].len = bytes; + + offset += bytes; + len -= bytes;drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 175
Quoted 10 lines
drivers/net/hyperv/netvsc_drv.c +10
-0+ +if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + } + } + +return j +1 ; +}I think this function has some edge case errors.
As I understand it, this function returns how many page buffer entries were filled in.
Quoted 7 lines
Quoted 2 lines
But if we fill exactly the end of a page, we will report one too many in the return value.
I may be missing something here; but in the case you are describing we would return a value of one as we should. When offset + len is exactly equal to the page size, we would not increment the value of j, since at that point len == 0. Since the initial value of j was 0 and we return (j + 1), we will return 1.
That's not what happens, you execute the loop code at least once, and hit:
Quoted 5 lines
Quoted 5 lines
drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 171
Quoted 5 lines
drivers/net/hyperv/netvsc_drv.c +5
-0+if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + }
thus incrementing j before the return statement.
RE: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/O
Quoted 83 lines
-----Original Message-----
From: David Miller [mailto:[email protected]]
Sent: Saturday, March 8, 2014 12:07 PM
To: KY Srinivasan
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/OFrom: KY Srinivasan <[email protected]>
Date: Sat, 8 Mar 2014 04:12:01 +0000Quoted 57 lines
Quoted 57 lines
-----Original Message-----
From: David Miller [mailto:[email protected]]
Sent: Saturday, March 8, 2014 3:18 AM
To: KY Srinivasan
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/OFrom: "K. Y. Srinivasan" <[email protected]>
Date: Thu, 6 Mar 2014 21:32:36 -0800drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 151
Quoted 9 lines
drivers/net/hyperv/netvsc_drv.c +9
-0+static u32 fill_pg_buf (struct page *page,u32 offset,u32 len, +struct hv_page_buffer *pb) +{ +int j =0 ; + +/* Deal with compund pages by ignoring unused part +* of the page. +*/ + page += offset >>PAGE_SHIFT ;Please only one space between "offset" and ">>"
Quoted 14 lines
drivers/net/hyperv/netvsc_drv.c +14
-0+ offset &= ~PAGE_MASK ; + +while (len >0 ) { +unsigned long bytes; + + bytes =PAGE_SIZE - offset; +if (bytes > len) + bytes = len; + pb[j].pfn =page_to_pfn (page); + pb[j].offset = offset; + pb[j].len = bytes; + + offset += bytes; + len -= bytes;drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 175
Quoted 10 lines
drivers/net/hyperv/netvsc_drv.c +10
-0+ +if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + } + } + +return j +1 ; +}I think this function has some edge case errors.
As I understand it, this function returns how many page buffer entries were filled in.
Quoted 7 lines
Quoted 2 lines
But if we fill exactly the end of a page, we will report one too many in the return value.
I may be missing something here; but in the case you are describing we would return a value of one as we should. When offset + len is exactly equal to the page size, we would not increment the value of j, since at that point len == 0. Since the initial value of j was 0 and we return (j + 1),
we will return 1.
That's not what happens, you execute the loop code at least once, and hit:
Quoted 10 lines
Quoted 7 lines
Quoted 7 lines
drivers/net/hyperv/netvsc_drv.c static void netvsc_xmit_completion(void *context) · line 171
Quoted 7 lines
drivers/net/hyperv/netvsc_drv.c +7
-0+ offset += bytes; + len -= bytes; +if (offset ==PAGE_SIZE && len) { + page++; + offset =0 ; + j++; + }thus incrementing j before the return statement.
I don't mean to be argumentative, but I still don't see the issue here. You are right, I will execute the loop code at least once, but j gets incremented only if len is non-zero as well as offset being == PAGE_SIZE. "len" would have gotten decremented prior to the check and in the case we are talking about, "len" would be zero and so j would not get incremented.
Regards,
K. Y
David Miller
maintainerRe: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/O
From: KY Srinivasan <[email protected]>
Date: Sat, 8 Mar 2014 10:27:54 +0000
Quoted 3 lines
"len" would have gotten decremented prior to the check and in the case we are talking about, "len" would be zero and so j would not get incremented.
You are correct, this is the part of the logic in this case which I missed.
Thanks.