Skip to contentSkip to conversation

Drivers: net: hyperv: Enable various offloads

PATCH Superseded K. Y. Srinivasan 2 participants 12 messages Mar 7, 2014 – Mar 8, 2014

Subsystems (3)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index 9ef6be9..94271bf 100644
@@ -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, &ethtool_ops);
 	SET_NETDEV_DEV(net, &dev->device);
Signature · 1 line
1.7.4.1
#2 lore raw

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
index 39fc230..694bf7c 100644
@@ -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
#3 lore raw

PATCH v3 3/6 Drivers: net: hyperv: Enable offloads on the host

In reply to K. Y. Srinivasan

Prior 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
index 694bf7c..8bc4e76 100644
@@ -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
#4 lore raw

PATCH v2 4/6 Drivers: net: hyperv: Enable receive side IP checksum offload

In reply to K. Y. Srinivasan

Enable 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
index 8bc4e76..faeb746 100644
@@ -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
index 5769f65..467bb3c 100644
@@ -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, &ethtool_ops);
 	SET_NETDEV_DEV(net, &dev->device);
drivers/net/hyperv/rndis_filter.c +3 -1
index 9b02f21..4a37e3d 100644
@@ -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
#5 lore raw

PATCH v2 5/6 Drivers: net: hyperv: Enable send side checksum offload

In reply to K. Y. Srinivasan

Enable 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
index faeb746..4cf2382 100644
@@ -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
#6 lore raw

PATCH v2 6/6 Drivers: net: hyperv: Enable large send offload

In reply to K. Y. Srinivasan

Enable 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
index 4cf2382..7d06b49 100644
@@ -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
index affee1b..6774baf 100644
@@ -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);
Signature · 1 line
1.7.4.1
maintainer
#7 lore raw

Re: [PATCH V2 1/6] Drivers: net: hyperv: Enable scatter gather I/O

In reply to K. Y. Srinivasan

From: "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".

#8 lore raw

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/O

From: "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.

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".

maintainer
#9 lore raw

Re: [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/O

From: "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.

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.

#10 lore raw

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/O

From: KY Srinivasan <[email protected]>
Date: Sat, 8 Mar 2014 04:12:01 +0000

Quoted 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/O

From: "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.

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

maintainer
#11 lore raw

Re: [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.