All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing
@ 2014-08-04 13:37 Zoltan Kiss
  2014-08-04 13:37 ` [PATCH net-next 1/4 v5] pktgen: Fill the payload optionally with a pattern Zoltan Kiss
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: Zoltan Kiss, David S. Miller, Thomas Graf, Joe Perches, netdev,
	linux-kernel, xen-devel

This series is my work during stressing the xen network drivers, hopefully
others can benefit from it.
1/4: Filling payload with pattern, useful for checking data correctness when the
packets travelling without checksum (~ intrahost VM-to-VM). Already sent it
separately few times, so its version number differs
2/4: fine-grained buffer geometrics
3/4: fix UDP checksuming
4/4: sending TCP GSO packets. This is an RFC yet, as I'm not certain about all
things changed here

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org

v2: respin for net-next changes

v3:
- code style changes and smaller bugfixes
- new patch to fix UDP checksuming
- rework TCP checksuming and GSO in the last patch

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next 1/4 v5] pktgen: Fill the payload optionally with a pattern
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
  2014-08-04 13:37 ` [PATCH net-next 1/4 v5] pktgen: Fill the payload optionally with a pattern Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-04 13:37 ` [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually Zoltan Kiss
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: Zoltan Kiss, David S. Miller, Thomas Graf, Joe Perches, netdev,
	linux-kernel, xen-devel

Introduces a new flag called PATTERN, which puts a non-periodic, predicatble
pattern into the payload. This was useful to reproduce an otherwise intermittent
bug in xen-netback [1], where checksum checking doesn't help.
The pattern is a repetition of "%lu ", a series of increasing numbers divided by
space. The value of the number is the size of the preceding payload area. E.g.
"1 3 5 "..." 1000 1005 1010"
If the pattern is used, every frag will have its own page, unlike before, so it
needs more memory.

[1] 5837574: xen-netback: Fix grant ref resolution in RX path

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v2: some bugfixes for pattern_to_packet, as my upcoming patch revealed some

v3:
- move the space in the pattern after the number, it just feels more intuitive
- show this setting when the flags are queried

v4:
- keep the zero flag always

v5:
- indentation fix
- fix handling *incomplete in pattern_to_packet()

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 8b849dd..ab30856 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -202,6 +202,7 @@
 #define F_QUEUE_MAP_CPU (1<<14)	/* queue map mirrors smp_processor_id() */
 #define F_NODE          (1<<15)	/* Node memory alloc*/
 #define F_UDPCSUM       (1<<16)	/* Include UDP checksum */
+#define F_PATTERN       (1<<17)	/* Fill the payload with a pattern */
 
 /* Thread control flag bits */
 #define T_STOP        (1<<0)	/* Stop run */
@@ -257,7 +258,7 @@ struct pktgen_dev {
 	int max_pkt_size;
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
-	struct page *page;
+	struct page *pages[MAX_SKB_FRAGS];
 	u64 delay;		/* nano-seconds */
 
 	__u64 count;		/* Default No packets to send */
@@ -638,6 +639,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 	if (pkt_dev->flags & F_UDPCSUM)
 		seq_puts(seq, "UDPCSUM  ");
 
+	if (pkt_dev->flags & F_PATTERN)
+		seq_puts(seq, "PATTERN  ");
+
 	if (pkt_dev->flags & F_MPLS_RND)
 		seq_puts(seq,  "MPLS_RND  ");
 
@@ -1128,12 +1132,14 @@ static ssize_t pktgen_if_write(struct file *file,
 		i += len;
 
 		if (node_possible(value)) {
+			int j;
 			pkt_dev->node = value;
 			sprintf(pg_result, "OK: node=%d", pkt_dev->node);
-			if (pkt_dev->page) {
-				put_page(pkt_dev->page);
-				pkt_dev->page = NULL;
-			}
+			for (j = 0; j < MAX_SKB_FRAGS; ++j)
+				if (pkt_dev->pages[j]) {
+					put_page(pkt_dev->pages[j]);
+					pkt_dev->pages[j] = NULL;
+				}
 		}
 		else
 			sprintf(pg_result, "ERROR: node not possible");
@@ -1243,6 +1249,12 @@ static ssize_t pktgen_if_write(struct file *file,
 		else if (strcmp(f, "!UDPCSUM") == 0)
 			pkt_dev->flags &= ~F_UDPCSUM;
 
+		else if (strcmp(f, "PATTERN") == 0)
+			pkt_dev->flags |= F_PATTERN;
+
+		else if (strcmp(f, "!PATTERN") == 0)
+			pkt_dev->flags &= ~F_PATTERN;
+
 		else {
 			sprintf(pg_result,
 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
@@ -2624,17 +2636,101 @@ static inline __be16 build_tci(unsigned int id, unsigned int cfi,
 	return htons(id | (cfi << 12) | (prio << 13));
 }
 
+/* Max number of digits. The sizeof equals to log base 2^8 (UINT_MAX), multiply
+ * with 3 is a cheap, rounded up conversion to log10
+ */
+#define UINT_MAX_DIGITS (3*sizeof(unsigned long)+1)
+
+/* Fill the buffer up with a pattern
+ * buf - pointer to buffer
+ * bufsize - size of the buffer
+ * start - starting value for the pattern
+ * incomplete - pointer to the offset inside the pattern, or 0 if none
+ *
+ * The pattern is a repetition of " %lu", a series of increasing numbers divided
+ * by space. The value of the number is "start" plus the size of the preceding
+ * space. E.g. if start is 1000, it starts like " 1000 1005 1010".
+ * If the last number doesn't fit, it gets truncated, and the number of leading
+ * characters is saved into incomplete. It can be passed then to the next call
+ * with the next buffer, and the pattern looks contiguous over scattered
+ * buffers.
+ * It returns "start" plus the offset of the byte after the last full
+ * pattern write.
+ */
+static unsigned long pattern_to_packet(char *buf,
+				       int bufsize,
+				       unsigned long start,
+				       unsigned int *incomplete)
+{
+	int len;
+	/* Only used when the pattern doesn't align to the buffer */
+	char temp[UINT_MAX_DIGITS];
+
+	if (*incomplete) {
+		int copylen, offset = *incomplete;
+
+		len = snprintf(temp, sizeof(temp), "%lu ", start);
+		copylen = len - *incomplete;
+		if (copylen > bufsize) {
+			/* The continuation of this number couldn't fit here */
+			copylen = bufsize;
+			*incomplete += bufsize;
+		} else {
+			*incomplete = 0;
+			start += len;
+		}
+		BUG_ON(offset + copylen > sizeof(temp));
+		memcpy(buf, temp + offset, copylen);
+		bufsize -= copylen;
+		buf += copylen;
+	}
+
+	while (bufsize > 0) {
+		BUG_ON(*incomplete);
+		len = snprintf(buf, bufsize, " %lu", start);
+		/* The last number doesn't fit, remember where it was truncated.
+		 */
+		if (len >= bufsize) {
+			/* snprintf always add a trailing zero, but actually we
+			 * need the last digit there
+			 */
+			len = snprintf(temp, sizeof(temp), " %lu", start);
+			memcpy(buf, temp, bufsize);
+			/* If the last number just fit without the trailing
+			 * zero, the next buffer can continue from an increased
+			 * offset.
+			 */
+			if (len == bufsize)
+				start += len;
+			else
+				*incomplete = bufsize;
+			return start;
+		}
+		bufsize -= len;
+		start += len;
+		buf += len;
+	}
+	return start;
+}
+
 static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 				int datalen)
 {
 	struct timeval timestamp;
 	struct pktgen_hdr *pgh;
+	unsigned long offset = 0;
+	unsigned int incomplete = 0;
 
 	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
 	datalen -= sizeof(*pgh);
 
 	if (pkt_dev->nfrags <= 0) {
-		memset(skb_put(skb, datalen), 0, datalen);
+		if (pkt_dev->flags & F_PATTERN)
+			offset = pattern_to_packet(skb_put(skb, datalen),
+						   datalen, offset,
+						   &incomplete);
+		else
+			memset(skb_put(skb, datalen), 0, datalen);
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;
@@ -2645,7 +2741,12 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			frags = MAX_SKB_FRAGS;
 		len = datalen - frags * PAGE_SIZE;
 		if (len > 0) {
-			memset(skb_put(skb, len), 0, len);
+			if (pkt_dev->flags & F_PATTERN)
+				offset = pattern_to_packet(skb_put(skb, len),
+							   len, offset,
+							   &incomplete);
+			else
+				memset(skb_put(skb, len), 0, len);
 			datalen = frags * PAGE_SIZE;
 		}
 
@@ -2653,17 +2754,27 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 		frag_len = (datalen/frags) < PAGE_SIZE ?
 			   (datalen/frags) : PAGE_SIZE;
 		while (datalen > 0) {
-			if (unlikely(!pkt_dev->page)) {
+			int fragpage;
+			gfp_t flags = GFP_KERNEL | __GFP_ZERO;
+
+			if (pkt_dev->flags & F_PATTERN)
+				fragpage = i;
+			else
+				fragpage = 0;
+
+			if (unlikely(!pkt_dev->pages[fragpage])) {
 				int node = numa_node_id();
 
 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
 					node = pkt_dev->node;
-				pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
-				if (!pkt_dev->page)
+				pkt_dev->pages[fragpage] =
+					alloc_pages_node(node, flags, 0);
+				if (!pkt_dev->pages[fragpage])
 					break;
 			}
-			get_page(pkt_dev->page);
-			skb_frag_set_page(skb, i, pkt_dev->page);
+			get_page(pkt_dev->pages[fragpage]);
+			skb_frag_set_page(skb, i, pkt_dev->pages[fragpage]);
+
 			skb_shinfo(skb)->frags[i].page_offset = 0;
 			/*last fragment, fill rest of data*/
 			if (i == (frags - 1))
@@ -2672,6 +2783,10 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			else
 				skb_frag_size_set(&skb_shinfo(skb)->frags[i], frag_len);
 			datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
+			if (pkt_dev->flags & F_PATTERN)
+				offset = pattern_to_packet(skb_frag_address(&skb_shinfo(skb)->frags[i]),
+							   skb_frag_size(&skb_shinfo(skb)->frags[i]),
+							   offset, &incomplete);
 			skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
 			skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
 			i++;
@@ -3681,6 +3796,8 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t,
 static int pktgen_remove_device(struct pktgen_thread *t,
 				struct pktgen_dev *pkt_dev)
 {
+	int i;
+
 	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);
 
 	if (pkt_dev->running) {
@@ -3708,8 +3825,9 @@ static int pktgen_remove_device(struct pktgen_thread *t,
 	free_SAs(pkt_dev);
 #endif
 	vfree(pkt_dev->flows);
-	if (pkt_dev->page)
-		put_page(pkt_dev->page);
+	for (i = 0; i < MAX_SKB_FRAGS; ++i)
+		if (pkt_dev->pages[i])
+			put_page(pkt_dev->pages[i]);
 	kfree_rcu(pkt_dev, rcu);
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next 1/4 v5] pktgen: Fill the payload optionally with a pattern
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-04 13:37 ` Zoltan Kiss
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: netdev, linux-kernel, Joe Perches, Zoltan Kiss, Thomas Graf,
	xen-devel, David S. Miller

Introduces a new flag called PATTERN, which puts a non-periodic, predicatble
pattern into the payload. This was useful to reproduce an otherwise intermittent
bug in xen-netback [1], where checksum checking doesn't help.
The pattern is a repetition of "%lu ", a series of increasing numbers divided by
space. The value of the number is the size of the preceding payload area. E.g.
"1 3 5 "..." 1000 1005 1010"
If the pattern is used, every frag will have its own page, unlike before, so it
needs more memory.

[1] 5837574: xen-netback: Fix grant ref resolution in RX path

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v2: some bugfixes for pattern_to_packet, as my upcoming patch revealed some

v3:
- move the space in the pattern after the number, it just feels more intuitive
- show this setting when the flags are queried

v4:
- keep the zero flag always

v5:
- indentation fix
- fix handling *incomplete in pattern_to_packet()

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 8b849dd..ab30856 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -202,6 +202,7 @@
 #define F_QUEUE_MAP_CPU (1<<14)	/* queue map mirrors smp_processor_id() */
 #define F_NODE          (1<<15)	/* Node memory alloc*/
 #define F_UDPCSUM       (1<<16)	/* Include UDP checksum */
+#define F_PATTERN       (1<<17)	/* Fill the payload with a pattern */
 
 /* Thread control flag bits */
 #define T_STOP        (1<<0)	/* Stop run */
@@ -257,7 +258,7 @@ struct pktgen_dev {
 	int max_pkt_size;
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
-	struct page *page;
+	struct page *pages[MAX_SKB_FRAGS];
 	u64 delay;		/* nano-seconds */
 
 	__u64 count;		/* Default No packets to send */
@@ -638,6 +639,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 	if (pkt_dev->flags & F_UDPCSUM)
 		seq_puts(seq, "UDPCSUM  ");
 
+	if (pkt_dev->flags & F_PATTERN)
+		seq_puts(seq, "PATTERN  ");
+
 	if (pkt_dev->flags & F_MPLS_RND)
 		seq_puts(seq,  "MPLS_RND  ");
 
@@ -1128,12 +1132,14 @@ static ssize_t pktgen_if_write(struct file *file,
 		i += len;
 
 		if (node_possible(value)) {
+			int j;
 			pkt_dev->node = value;
 			sprintf(pg_result, "OK: node=%d", pkt_dev->node);
-			if (pkt_dev->page) {
-				put_page(pkt_dev->page);
-				pkt_dev->page = NULL;
-			}
+			for (j = 0; j < MAX_SKB_FRAGS; ++j)
+				if (pkt_dev->pages[j]) {
+					put_page(pkt_dev->pages[j]);
+					pkt_dev->pages[j] = NULL;
+				}
 		}
 		else
 			sprintf(pg_result, "ERROR: node not possible");
@@ -1243,6 +1249,12 @@ static ssize_t pktgen_if_write(struct file *file,
 		else if (strcmp(f, "!UDPCSUM") == 0)
 			pkt_dev->flags &= ~F_UDPCSUM;
 
+		else if (strcmp(f, "PATTERN") == 0)
+			pkt_dev->flags |= F_PATTERN;
+
+		else if (strcmp(f, "!PATTERN") == 0)
+			pkt_dev->flags &= ~F_PATTERN;
+
 		else {
 			sprintf(pg_result,
 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
@@ -2624,17 +2636,101 @@ static inline __be16 build_tci(unsigned int id, unsigned int cfi,
 	return htons(id | (cfi << 12) | (prio << 13));
 }
 
+/* Max number of digits. The sizeof equals to log base 2^8 (UINT_MAX), multiply
+ * with 3 is a cheap, rounded up conversion to log10
+ */
+#define UINT_MAX_DIGITS (3*sizeof(unsigned long)+1)
+
+/* Fill the buffer up with a pattern
+ * buf - pointer to buffer
+ * bufsize - size of the buffer
+ * start - starting value for the pattern
+ * incomplete - pointer to the offset inside the pattern, or 0 if none
+ *
+ * The pattern is a repetition of " %lu", a series of increasing numbers divided
+ * by space. The value of the number is "start" plus the size of the preceding
+ * space. E.g. if start is 1000, it starts like " 1000 1005 1010".
+ * If the last number doesn't fit, it gets truncated, and the number of leading
+ * characters is saved into incomplete. It can be passed then to the next call
+ * with the next buffer, and the pattern looks contiguous over scattered
+ * buffers.
+ * It returns "start" plus the offset of the byte after the last full
+ * pattern write.
+ */
+static unsigned long pattern_to_packet(char *buf,
+				       int bufsize,
+				       unsigned long start,
+				       unsigned int *incomplete)
+{
+	int len;
+	/* Only used when the pattern doesn't align to the buffer */
+	char temp[UINT_MAX_DIGITS];
+
+	if (*incomplete) {
+		int copylen, offset = *incomplete;
+
+		len = snprintf(temp, sizeof(temp), "%lu ", start);
+		copylen = len - *incomplete;
+		if (copylen > bufsize) {
+			/* The continuation of this number couldn't fit here */
+			copylen = bufsize;
+			*incomplete += bufsize;
+		} else {
+			*incomplete = 0;
+			start += len;
+		}
+		BUG_ON(offset + copylen > sizeof(temp));
+		memcpy(buf, temp + offset, copylen);
+		bufsize -= copylen;
+		buf += copylen;
+	}
+
+	while (bufsize > 0) {
+		BUG_ON(*incomplete);
+		len = snprintf(buf, bufsize, " %lu", start);
+		/* The last number doesn't fit, remember where it was truncated.
+		 */
+		if (len >= bufsize) {
+			/* snprintf always add a trailing zero, but actually we
+			 * need the last digit there
+			 */
+			len = snprintf(temp, sizeof(temp), " %lu", start);
+			memcpy(buf, temp, bufsize);
+			/* If the last number just fit without the trailing
+			 * zero, the next buffer can continue from an increased
+			 * offset.
+			 */
+			if (len == bufsize)
+				start += len;
+			else
+				*incomplete = bufsize;
+			return start;
+		}
+		bufsize -= len;
+		start += len;
+		buf += len;
+	}
+	return start;
+}
+
 static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 				int datalen)
 {
 	struct timeval timestamp;
 	struct pktgen_hdr *pgh;
+	unsigned long offset = 0;
+	unsigned int incomplete = 0;
 
 	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
 	datalen -= sizeof(*pgh);
 
 	if (pkt_dev->nfrags <= 0) {
-		memset(skb_put(skb, datalen), 0, datalen);
+		if (pkt_dev->flags & F_PATTERN)
+			offset = pattern_to_packet(skb_put(skb, datalen),
+						   datalen, offset,
+						   &incomplete);
+		else
+			memset(skb_put(skb, datalen), 0, datalen);
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;
@@ -2645,7 +2741,12 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			frags = MAX_SKB_FRAGS;
 		len = datalen - frags * PAGE_SIZE;
 		if (len > 0) {
-			memset(skb_put(skb, len), 0, len);
+			if (pkt_dev->flags & F_PATTERN)
+				offset = pattern_to_packet(skb_put(skb, len),
+							   len, offset,
+							   &incomplete);
+			else
+				memset(skb_put(skb, len), 0, len);
 			datalen = frags * PAGE_SIZE;
 		}
 
@@ -2653,17 +2754,27 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 		frag_len = (datalen/frags) < PAGE_SIZE ?
 			   (datalen/frags) : PAGE_SIZE;
 		while (datalen > 0) {
-			if (unlikely(!pkt_dev->page)) {
+			int fragpage;
+			gfp_t flags = GFP_KERNEL | __GFP_ZERO;
+
+			if (pkt_dev->flags & F_PATTERN)
+				fragpage = i;
+			else
+				fragpage = 0;
+
+			if (unlikely(!pkt_dev->pages[fragpage])) {
 				int node = numa_node_id();
 
 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
 					node = pkt_dev->node;
-				pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
-				if (!pkt_dev->page)
+				pkt_dev->pages[fragpage] =
+					alloc_pages_node(node, flags, 0);
+				if (!pkt_dev->pages[fragpage])
 					break;
 			}
-			get_page(pkt_dev->page);
-			skb_frag_set_page(skb, i, pkt_dev->page);
+			get_page(pkt_dev->pages[fragpage]);
+			skb_frag_set_page(skb, i, pkt_dev->pages[fragpage]);
+
 			skb_shinfo(skb)->frags[i].page_offset = 0;
 			/*last fragment, fill rest of data*/
 			if (i == (frags - 1))
@@ -2672,6 +2783,10 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			else
 				skb_frag_size_set(&skb_shinfo(skb)->frags[i], frag_len);
 			datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
+			if (pkt_dev->flags & F_PATTERN)
+				offset = pattern_to_packet(skb_frag_address(&skb_shinfo(skb)->frags[i]),
+							   skb_frag_size(&skb_shinfo(skb)->frags[i]),
+							   offset, &incomplete);
 			skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
 			skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
 			i++;
@@ -3681,6 +3796,8 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t,
 static int pktgen_remove_device(struct pktgen_thread *t,
 				struct pktgen_dev *pkt_dev)
 {
+	int i;
+
 	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);
 
 	if (pkt_dev->running) {
@@ -3708,8 +3825,9 @@ static int pktgen_remove_device(struct pktgen_thread *t,
 	free_SAs(pkt_dev);
 #endif
 	vfree(pkt_dev->flows);
-	if (pkt_dev->page)
-		put_page(pkt_dev->page);
+	for (i = 0; i < MAX_SKB_FRAGS; ++i)
+		if (pkt_dev->pages[i])
+			put_page(pkt_dev->pages[i]);
 	kfree_rcu(pkt_dev, rcu);
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
                   ` (2 preceding siblings ...)
  2014-08-04 13:37 ` [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-04 13:37 ` [PATCH net-next 3/4 RFC] pktgen: Fixing UPD checksum calculation Zoltan Kiss
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: Zoltan Kiss, David S. Miller, Thomas Graf, Joe Perches, netdev,
	linux-kernel, xen-devel

By defining the number of frags via "nfrags", their sizes get calculated by
pktgen. This patch allows their offsets and sizes to be specified via
"frag_off-len", in a comma separated list (e.g.
"frag_off-len 0-1,500-200,5000-10,9-100). The first is the offset
(0 <= offset < 2^16), second is size (0 < length <= 65536). This also determines
the number of frags, so it overwrites "frags" (and vice versa, "frags"
invalidate this setting)
xen-netback is prone to have problem with compound pages, as the memory granting
interface can only handle 4k pages. This extension of pktgen is proven to be
useful to test that.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v2: allocate new compound page only if order is smaller

v3: fix up indexing and reseting of pages array

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ab30856..a04d9d4 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -236,6 +236,11 @@ struct flow_state {
 /* flow flag bits */
 #define F_INIT   (1<<0)		/* flow has been initialized */
 
+struct frag_param {
+	int offset;
+	int length;
+};
+
 struct pktgen_dev {
 	/*
 	 * Try to keep frequent/infrequent used vars. separated.
@@ -258,6 +263,11 @@ struct pktgen_dev {
 	int max_pkt_size;
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
+	/* offset-length pairs for the frags, only the first nfrags are valid,
+	 * and only if frags[0].length != 0, as zero frag size is not valid
+	 * (and neither negative)
+	 */
+	struct frag_param frags[MAX_SKB_FRAGS];
 	struct page *pages[MAX_SKB_FRAGS];
 	u64 delay;		/* nano-seconds */
 
@@ -541,6 +551,18 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
 		   pkt_dev->clone_skb, pkt_dev->odevname);
 
+	if (pkt_dev->frags[0].length) {
+		int n;
+
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			seq_printf(seq, "     %d. offset: %d length: %d%s\n", n,
+				   pkt_dev->frags[n].offset,
+				   pkt_dev->frags[n].length,
+				   n == pkt_dev->nfrags-1 ? "" : ",");
+	} else {
+			seq_puts(seq, "     No frag parameters defined\n");
+	}
+
 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
 		   pkt_dev->lflow);
 
@@ -845,6 +867,50 @@ static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
 	return i;
 }
 
+static ssize_t get_sizes(const char __user *buffer, struct pktgen_dev *pkt_dev)
+{
+	unsigned int n = 0;
+	char c;
+	ssize_t i = 0;
+	int len;
+
+	pkt_dev->nfrags = 0;
+	do {
+		unsigned long tmp;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		/* Maximum skb size minus 1 */
+		if (tmp >= 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].offset = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		if (c != '-')
+			return -EINVAL;
+		i++;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		if (tmp < 1 || tmp > 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].length = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		i++;
+		n++;
+		if (n > MAX_SKB_FRAGS)
+			return -E2BIG;
+	} while (c == ',');
+
+	pkt_dev->nfrags = n;
+	return i;
+}
+
 static ssize_t pktgen_if_write(struct file *file,
 			       const char __user * user_buffer, size_t count,
 			       loff_t * offset)
@@ -972,9 +1038,30 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		i += len;
 		pkt_dev->nfrags = value;
+		/* Invalidate whatever was specified before */
+		pkt_dev->frags[0].length = 0;
 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
 		return count;
 	}
+
+	if (!strcmp(name, "frag_off-len")) {
+		unsigned int n, cnt;
+
+		len = get_sizes(&user_buffer[i], pkt_dev);
+		if (len < 0)
+			return len;
+		i += len;
+		cnt = sprintf(pg_result, "OK: frags=%u frag offsets-sizes=",
+			      pkt_dev->nfrags);
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			cnt += sprintf(pg_result + cnt, "%d-%d%s",
+				       pkt_dev->frags[n].offset,
+				       pkt_dev->frags[n].length,
+				       n == pkt_dev->nfrags-1 ? "" : ",");
+
+		return count;
+	}
+
 	if (!strcmp(name, "delay")) {
 		len = num_arg(&user_buffer[i], 10, &value);
 		if (len < 0)
@@ -2734,12 +2821,25 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;
-		int frag_len;
+		int frag_len = 0;
 
 
 		if (frags > MAX_SKB_FRAGS)
 			frags = MAX_SKB_FRAGS;
-		len = datalen - frags * PAGE_SIZE;
+
+		if (pkt_dev->frags[0].length) {
+			for (i = 0; i < frags; ++i)
+				frag_len += pkt_dev->frags[i].length;
+			if (frag_len > datalen) {
+				pr_err("Payload length (%d) smaller than frags (%d)\n",
+				       datalen, frag_len);
+				return;
+			}
+		} else {
+			frag_len = frags * PAGE_SIZE;
+		}
+
+		len = datalen - frag_len;
 		if (len > 0) {
 			if (pkt_dev->flags & F_PATTERN)
 				offset = pattern_to_packet(skb_put(skb, len),
@@ -2747,28 +2847,44 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 							   &incomplete);
 			else
 				memset(skb_put(skb, len), 0, len);
-			datalen = frags * PAGE_SIZE;
+			datalen = frag_len;
 		}
 
 		i = 0;
 		frag_len = (datalen/frags) < PAGE_SIZE ?
 			   (datalen/frags) : PAGE_SIZE;
 		while (datalen > 0) {
-			int fragpage;
+			int fragpage, order = 0;
 			gfp_t flags = GFP_KERNEL | __GFP_ZERO;
 
-			if (pkt_dev->flags & F_PATTERN)
+			if (pkt_dev->flags & F_PATTERN ||
+			    pkt_dev->frags[0].length)
 				fragpage = i;
 			else
 				fragpage = 0;
 
+			/* Free this page if we gonna need a different order */
+			if (pkt_dev->frags[0].length) {
+				int max_off = pkt_dev->frags[i].offset +
+					      pkt_dev->frags[i].length;
+				order = get_order(max_off);
+				if (pkt_dev->pages[fragpage] &&
+				    order != compound_order(pkt_dev->pages[fragpage])) {
+					if (pkt_dev->pages[fragpage]) {
+						put_page(pkt_dev->pages[fragpage]);
+						pkt_dev->pages[fragpage] = NULL;
+					}
+				}
+			}
+
 			if (unlikely(!pkt_dev->pages[fragpage])) {
 				int node = numa_node_id();
 
 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
 					node = pkt_dev->node;
+				order ? flags |= __GFP_COMP : 0;
 				pkt_dev->pages[fragpage] =
-					alloc_pages_node(node, flags, 0);
+					alloc_pages_node(node, flags, order);
 				if (!pkt_dev->pages[fragpage])
 					break;
 			}
@@ -2776,8 +2892,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			skb_frag_set_page(skb, i, pkt_dev->pages[fragpage]);
 
 			skb_shinfo(skb)->frags[i].page_offset = 0;
+			if (pkt_dev->frags[0].length) {
+				skb_shinfo(skb)->frags[i].page_offset =
+					pkt_dev->frags[i].offset;
+				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
+						  pkt_dev->frags[i].length);
+			}
 			/*last fragment, fill rest of data*/
-			if (i == (frags - 1))
+			else if (i == (frags - 1))
 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
 			else
@@ -3826,8 +3948,10 @@ static int pktgen_remove_device(struct pktgen_thread *t,
 #endif
 	vfree(pkt_dev->flows);
 	for (i = 0; i < MAX_SKB_FRAGS; ++i)
-		if (pkt_dev->pages[i])
+		if (pkt_dev->pages[i]) {
 			put_page(pkt_dev->pages[i]);
+			pkt_dev->pages[i] = NULL;
+		}
 	kfree_rcu(pkt_dev, rcu);
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
  2014-08-04 13:37 ` [PATCH net-next 1/4 v5] pktgen: Fill the payload optionally with a pattern Zoltan Kiss
  2014-08-04 13:37 ` Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-04 13:37 ` Zoltan Kiss
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: netdev, linux-kernel, Joe Perches, Zoltan Kiss, Thomas Graf,
	xen-devel, David S. Miller

By defining the number of frags via "nfrags", their sizes get calculated by
pktgen. This patch allows their offsets and sizes to be specified via
"frag_off-len", in a comma separated list (e.g.
"frag_off-len 0-1,500-200,5000-10,9-100). The first is the offset
(0 <= offset < 2^16), second is size (0 < length <= 65536). This also determines
the number of frags, so it overwrites "frags" (and vice versa, "frags"
invalidate this setting)
xen-netback is prone to have problem with compound pages, as the memory granting
interface can only handle 4k pages. This extension of pktgen is proven to be
useful to test that.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v2: allocate new compound page only if order is smaller

v3: fix up indexing and reseting of pages array

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ab30856..a04d9d4 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -236,6 +236,11 @@ struct flow_state {
 /* flow flag bits */
 #define F_INIT   (1<<0)		/* flow has been initialized */
 
+struct frag_param {
+	int offset;
+	int length;
+};
+
 struct pktgen_dev {
 	/*
 	 * Try to keep frequent/infrequent used vars. separated.
@@ -258,6 +263,11 @@ struct pktgen_dev {
 	int max_pkt_size;
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
+	/* offset-length pairs for the frags, only the first nfrags are valid,
+	 * and only if frags[0].length != 0, as zero frag size is not valid
+	 * (and neither negative)
+	 */
+	struct frag_param frags[MAX_SKB_FRAGS];
 	struct page *pages[MAX_SKB_FRAGS];
 	u64 delay;		/* nano-seconds */
 
@@ -541,6 +551,18 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
 		   pkt_dev->clone_skb, pkt_dev->odevname);
 
+	if (pkt_dev->frags[0].length) {
+		int n;
+
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			seq_printf(seq, "     %d. offset: %d length: %d%s\n", n,
+				   pkt_dev->frags[n].offset,
+				   pkt_dev->frags[n].length,
+				   n == pkt_dev->nfrags-1 ? "" : ",");
+	} else {
+			seq_puts(seq, "     No frag parameters defined\n");
+	}
+
 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
 		   pkt_dev->lflow);
 
@@ -845,6 +867,50 @@ static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
 	return i;
 }
 
+static ssize_t get_sizes(const char __user *buffer, struct pktgen_dev *pkt_dev)
+{
+	unsigned int n = 0;
+	char c;
+	ssize_t i = 0;
+	int len;
+
+	pkt_dev->nfrags = 0;
+	do {
+		unsigned long tmp;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		/* Maximum skb size minus 1 */
+		if (tmp >= 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].offset = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		if (c != '-')
+			return -EINVAL;
+		i++;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		if (tmp < 1 || tmp > 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].length = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		i++;
+		n++;
+		if (n > MAX_SKB_FRAGS)
+			return -E2BIG;
+	} while (c == ',');
+
+	pkt_dev->nfrags = n;
+	return i;
+}
+
 static ssize_t pktgen_if_write(struct file *file,
 			       const char __user * user_buffer, size_t count,
 			       loff_t * offset)
@@ -972,9 +1038,30 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		i += len;
 		pkt_dev->nfrags = value;
+		/* Invalidate whatever was specified before */
+		pkt_dev->frags[0].length = 0;
 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
 		return count;
 	}
+
+	if (!strcmp(name, "frag_off-len")) {
+		unsigned int n, cnt;
+
+		len = get_sizes(&user_buffer[i], pkt_dev);
+		if (len < 0)
+			return len;
+		i += len;
+		cnt = sprintf(pg_result, "OK: frags=%u frag offsets-sizes=",
+			      pkt_dev->nfrags);
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			cnt += sprintf(pg_result + cnt, "%d-%d%s",
+				       pkt_dev->frags[n].offset,
+				       pkt_dev->frags[n].length,
+				       n == pkt_dev->nfrags-1 ? "" : ",");
+
+		return count;
+	}
+
 	if (!strcmp(name, "delay")) {
 		len = num_arg(&user_buffer[i], 10, &value);
 		if (len < 0)
@@ -2734,12 +2821,25 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;
-		int frag_len;
+		int frag_len = 0;
 
 
 		if (frags > MAX_SKB_FRAGS)
 			frags = MAX_SKB_FRAGS;
-		len = datalen - frags * PAGE_SIZE;
+
+		if (pkt_dev->frags[0].length) {
+			for (i = 0; i < frags; ++i)
+				frag_len += pkt_dev->frags[i].length;
+			if (frag_len > datalen) {
+				pr_err("Payload length (%d) smaller than frags (%d)\n",
+				       datalen, frag_len);
+				return;
+			}
+		} else {
+			frag_len = frags * PAGE_SIZE;
+		}
+
+		len = datalen - frag_len;
 		if (len > 0) {
 			if (pkt_dev->flags & F_PATTERN)
 				offset = pattern_to_packet(skb_put(skb, len),
@@ -2747,28 +2847,44 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 							   &incomplete);
 			else
 				memset(skb_put(skb, len), 0, len);
-			datalen = frags * PAGE_SIZE;
+			datalen = frag_len;
 		}
 
 		i = 0;
 		frag_len = (datalen/frags) < PAGE_SIZE ?
 			   (datalen/frags) : PAGE_SIZE;
 		while (datalen > 0) {
-			int fragpage;
+			int fragpage, order = 0;
 			gfp_t flags = GFP_KERNEL | __GFP_ZERO;
 
-			if (pkt_dev->flags & F_PATTERN)
+			if (pkt_dev->flags & F_PATTERN ||
+			    pkt_dev->frags[0].length)
 				fragpage = i;
 			else
 				fragpage = 0;
 
+			/* Free this page if we gonna need a different order */
+			if (pkt_dev->frags[0].length) {
+				int max_off = pkt_dev->frags[i].offset +
+					      pkt_dev->frags[i].length;
+				order = get_order(max_off);
+				if (pkt_dev->pages[fragpage] &&
+				    order != compound_order(pkt_dev->pages[fragpage])) {
+					if (pkt_dev->pages[fragpage]) {
+						put_page(pkt_dev->pages[fragpage]);
+						pkt_dev->pages[fragpage] = NULL;
+					}
+				}
+			}
+
 			if (unlikely(!pkt_dev->pages[fragpage])) {
 				int node = numa_node_id();
 
 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
 					node = pkt_dev->node;
+				order ? flags |= __GFP_COMP : 0;
 				pkt_dev->pages[fragpage] =
-					alloc_pages_node(node, flags, 0);
+					alloc_pages_node(node, flags, order);
 				if (!pkt_dev->pages[fragpage])
 					break;
 			}
@@ -2776,8 +2892,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			skb_frag_set_page(skb, i, pkt_dev->pages[fragpage]);
 
 			skb_shinfo(skb)->frags[i].page_offset = 0;
+			if (pkt_dev->frags[0].length) {
+				skb_shinfo(skb)->frags[i].page_offset =
+					pkt_dev->frags[i].offset;
+				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
+						  pkt_dev->frags[i].length);
+			}
 			/*last fragment, fill rest of data*/
-			if (i == (frags - 1))
+			else if (i == (frags - 1))
 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
 			else
@@ -3826,8 +3948,10 @@ static int pktgen_remove_device(struct pktgen_thread *t,
 #endif
 	vfree(pkt_dev->flows);
 	for (i = 0; i < MAX_SKB_FRAGS; ++i)
-		if (pkt_dev->pages[i])
+		if (pkt_dev->pages[i]) {
 			put_page(pkt_dev->pages[i]);
+			pkt_dev->pages[i] = NULL;
+		}
 	kfree_rcu(pkt_dev, rcu);
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next 3/4 RFC] pktgen: Fixing UPD checksum calculation
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
                   ` (4 preceding siblings ...)
  2014-08-04 13:37 ` [PATCH net-next 3/4 RFC] pktgen: Fixing UPD checksum calculation Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-04 13:37 ` [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets Zoltan Kiss
  2014-08-04 13:37 ` Zoltan Kiss
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: Zoltan Kiss, David S. Miller, Thomas Graf, Joe Perches, netdev,
	linux-kernel, xen-devel

It passes port number instead of IP address for checksuming. The udp4_hwcsum()
call is also bad, but my next patch will get rid of it anyway. The IPv6 code
does it correctly already. It also changes replaces 8 with sizeof(struct udphdr) 

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ba61e2b..d8905cd 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3061,8 +3061,9 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 		__wsum csum = udp_csum(skb);
 
 		/* add protocol-dependent pseudo-header */
-		udph->check = csum_tcpudp_magic(udph->source, udph->dest,
-						datalen + 8, IPPROTO_UDP, csum);
+		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+						datalen + sizeof(struct udphdr),
+						IPPROTO_UDP, csum);
 
 		if (udph->check == 0)
 			udph->check = CSUM_MANGLED_0;

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next 3/4 RFC] pktgen: Fixing UPD checksum calculation
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
                   ` (3 preceding siblings ...)
  2014-08-04 13:37 ` Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-04 13:37 ` Zoltan Kiss
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: netdev, linux-kernel, Joe Perches, Zoltan Kiss, Thomas Graf,
	xen-devel, David S. Miller

It passes port number instead of IP address for checksuming. The udp4_hwcsum()
call is also bad, but my next patch will get rid of it anyway. The IPv6 code
does it correctly already. It also changes replaces 8 with sizeof(struct udphdr) 

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ba61e2b..d8905cd 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3061,8 +3061,9 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 		__wsum csum = udp_csum(skb);
 
 		/* add protocol-dependent pseudo-header */
-		udph->check = csum_tcpudp_magic(udph->source, udph->dest,
-						datalen + 8, IPPROTO_UDP, csum);
+		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+						datalen + sizeof(struct udphdr),
+						IPPROTO_UDP, csum);
 
 		if (udph->check == 0)
 			udph->check = CSUM_MANGLED_0;

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
                   ` (5 preceding siblings ...)
  2014-08-04 13:37 ` Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  2014-08-05 20:09   ` David Miller
  2014-08-05 20:09   ` David Miller
  2014-08-04 13:37 ` Zoltan Kiss
  7 siblings, 2 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: Zoltan Kiss, David S. Miller, Thomas Graf, Joe Perches, netdev,
	linux-kernel, xen-devel

This is a prototype patch to enable sending IPv4 TCP packets with pktgen. The
original motivation is to test TCP GSO with xen-netback/netfront, but I'm not
sure about how the checksum should be set up, and also someone should verify the
GSO settings I'm using.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v3:
- mention explicitly that this for IPv4
- memset the TCP header and set up doff
- rework of checksum handling and GSO setting in fill_packet_ipv4
- bail out in pktgen_xmit if the device won't be able to handle GSO

v4:
- set the transport headers in dedicated functions
- instead of fake sockets just duplicate the relevant parts of
  __tcp_v4_send_check

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 0d0aaac..c0c561f 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -162,6 +162,7 @@
 #include <net/checksum.h>
 #include <net/ipv6.h>
 #include <net/udp.h>
+#include <net/tcp.h>
 #include <net/ip6_checksum.h>
 #include <net/addrconf.h>
 #ifdef CONFIG_XFRM
@@ -203,6 +204,7 @@
 #define F_NODE          (1<<15)	/* Node memory alloc*/
 #define F_UDPCSUM       (1<<16)	/* Include UDP checksum */
 #define F_PATTERN       (1<<17)	/* Fill the payload with a pattern */
+#define F_TCP           (1<<18)	/* Send TCP packet instead of UDP */
 
 /* Thread control flag bits */
 #define T_STOP        (1<<0)	/* Stop run */
@@ -664,6 +666,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 	if (pkt_dev->flags & F_PATTERN)
 		seq_puts(seq, "PATTERN  ");
 
+	if (pkt_dev->flags & F_TCP)
+		seq_puts(seq, "TCP  ");
+
 	if (pkt_dev->flags & F_MPLS_RND)
 		seq_puts(seq,  "MPLS_RND  ");
 
@@ -1342,6 +1347,12 @@ static ssize_t pktgen_if_write(struct file *file,
 		else if (strcmp(f, "!PATTERN") == 0)
 			pkt_dev->flags &= ~F_PATTERN;
 
+		else if (strcmp(f, "TCP") == 0)
+			pkt_dev->flags |= F_TCP;
+
+		else if (strcmp(f, "!TCP") == 0)
+			pkt_dev->flags &= ~F_TCP;
+
 		else {
 			sprintf(pg_result,
 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
@@ -2950,12 +2961,39 @@ static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
 	return skb;
 }
 
+static int fill_packet_tcp(struct pktgen_dev *pkt_dev, struct sk_buff *skb)
+{
+	struct tcphdr *tcph =
+		(struct tcphdr *)skb_put(skb, sizeof(struct tcphdr));
+
+	memset(tcph, 0, sizeof(*tcph));
+	tcph->source = htons(pkt_dev->cur_udp_src);
+	tcph->dest = htons(pkt_dev->cur_udp_dst);
+	tcph->doff = sizeof(struct tcphdr) >> 2;
+	return pkt_dev->cur_pkt_size - ETH_HLEN - 20 - sizeof(struct tcphdr) -
+		pkt_dev->pkt_overhead;
+}
+
+static int fill_packet_udp(struct pktgen_dev *pkt_dev, struct sk_buff *skb)
+{
+	struct udphdr *udph =
+		(struct udphdr *)skb_put(skb, sizeof(struct udphdr));
+	int datalen = pkt_dev->cur_pkt_size - ETH_HLEN - 20 -
+		      sizeof(struct udphdr) - pkt_dev->pkt_overhead;
+
+	udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
+	udph->source = htons(pkt_dev->cur_udp_src);
+	udph->dest = htons(pkt_dev->cur_udp_dst);
+	udph->len = htons(datalen + sizeof(struct udphdr));
+	udph->check = 0;
+	return datalen;
+}
+
 static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 					struct pktgen_dev *pkt_dev)
 {
 	struct sk_buff *skb = NULL;
 	__u8 *eth;
-	struct udphdr *udph;
 	int datalen, iplen;
 	struct iphdr *iph;
 	__be16 protocol = htons(ETH_P_IP);
@@ -3017,29 +3055,27 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 	iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
 
 	skb_set_transport_header(skb, skb->len);
-	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
+
+	if (pkt_dev->flags & F_TCP)
+		datalen = fill_packet_tcp(pkt_dev, skb);
+	else
+		datalen = fill_packet_udp(pkt_dev, skb);
+
 	skb_set_queue_mapping(skb, queue_map);
 	skb->priority = pkt_dev->skb_priority;
 
 	memcpy(eth, pkt_dev->hh, 12);
 	*(__be16 *) & eth[12] = protocol;
 
-	/* Eth + IPh + UDPh + mpls */
-	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
-		  pkt_dev->pkt_overhead;
 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
 		datalen = sizeof(struct pktgen_hdr);
 
-	udph->source = htons(pkt_dev->cur_udp_src);
-	udph->dest = htons(pkt_dev->cur_udp_dst);
-	udph->len = htons(datalen + 8);	/* DATA + udphdr */
-	udph->check = 0;
 
 	iph->ihl = 5;
 	iph->version = 4;
 	iph->ttl = 32;
 	iph->tos = pkt_dev->tos;
-	iph->protocol = IPPROTO_UDP;	/* UDP */
+	iph->protocol = pkt_dev->flags & F_TCP ? IPPROTO_TCP : IPPROTO_UDP;
 	iph->saddr = pkt_dev->cur_saddr;
 	iph->daddr = pkt_dev->cur_daddr;
 	iph->id = htons(pkt_dev->ip_id);
@@ -3055,10 +3091,17 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 	if (!(pkt_dev->flags & F_UDPCSUM)) {
 		skb->ip_summed = CHECKSUM_NONE;
 	} else if (odev->features & NETIF_F_V4_CSUM) {
-		skb->ip_summed = CHECKSUM_PARTIAL;
-		skb->csum = 0;
-		udp4_hwcsum(skb, udph->source, udph->dest);
+		skb_checksum_setup(skb, true);
+	} else if (pkt_dev->flags & F_TCP) {
+		struct tcphdr *tcph = tcp_hdr(skb);
+
+		skb->ip_summed = CHECKSUM_NONE;
+		tcph->check = tcp_v4_check(skb->len, iph->saddr, iph->daddr,
+					   csum_partial(tcph, tcph->doff << 2,
+							skb->csum));
 	} else {
+		struct udphdr * const udph =
+			(struct udphdr *)skb_transport_header(skb);
 		__wsum csum = udp_csum(skb);
 
 		/* add protocol-dependent pseudo-header */
@@ -3072,6 +3115,20 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 
 	pktgen_finalize_skb(pkt_dev, skb, datalen);
 
+	if (odev->mtu < skb->len) {
+		int hdrlen = skb_transport_header(skb) - skb_mac_header(skb);
+
+		if (pkt_dev->flags & F_TCP) {
+			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+			hdrlen += tcp_hdrlen(skb);
+		} else {
+			skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+			hdrlen += sizeof(struct udphdr);
+		}
+		skb_shinfo(skb)->gso_size = odev->mtu - hdrlen;
+		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len - hdrlen, skb_shinfo(skb)->gso_size);
+	}
+
 #ifdef CONFIG_XFRM
 	if (!process_ipsec(pkt_dev, skb, protocol))
 		return NULL;
@@ -3559,6 +3616,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
 		pkt_dev->last_pkt_size = pkt_dev->skb->len;
 		pkt_dev->allocated_skbs++;
 		pkt_dev->clone_count = 0;	/* reset counter */
+
+		if (netif_needs_gso(pkt_dev->skb, netif_skb_features(pkt_dev->skb))) {
+			pr_err("Device don't have necessary GSO features! netif_skb_features: %llX summed %u\n",
+			       netif_skb_features(pkt_dev->skb),
+			       pkt_dev->skb->ip_summed);
+			pkt_dev->sofar++;
+			goto out;
+		}
 	}
 
 	if (pkt_dev->delay && pkt_dev->last_ok)
@@ -3608,7 +3673,7 @@ unlock:
 	HARD_TX_UNLOCK(odev, txq);
 
 	local_bh_enable();
-
+out:
 	/* If pkt_dev->count is zero, then run forever */
 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
 		pktgen_wait_for_skb(pkt_dev);

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets
  2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
                   ` (6 preceding siblings ...)
  2014-08-04 13:37 ` [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets Zoltan Kiss
@ 2014-08-04 13:37 ` Zoltan Kiss
  7 siblings, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-04 13:37 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: netdev, linux-kernel, Joe Perches, Zoltan Kiss, Thomas Graf,
	xen-devel, David S. Miller

This is a prototype patch to enable sending IPv4 TCP packets with pktgen. The
original motivation is to test TCP GSO with xen-netback/netfront, but I'm not
sure about how the checksum should be set up, and also someone should verify the
GSO settings I'm using.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v3:
- mention explicitly that this for IPv4
- memset the TCP header and set up doff
- rework of checksum handling and GSO setting in fill_packet_ipv4
- bail out in pktgen_xmit if the device won't be able to handle GSO

v4:
- set the transport headers in dedicated functions
- instead of fake sockets just duplicate the relevant parts of
  __tcp_v4_send_check

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 0d0aaac..c0c561f 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -162,6 +162,7 @@
 #include <net/checksum.h>
 #include <net/ipv6.h>
 #include <net/udp.h>
+#include <net/tcp.h>
 #include <net/ip6_checksum.h>
 #include <net/addrconf.h>
 #ifdef CONFIG_XFRM
@@ -203,6 +204,7 @@
 #define F_NODE          (1<<15)	/* Node memory alloc*/
 #define F_UDPCSUM       (1<<16)	/* Include UDP checksum */
 #define F_PATTERN       (1<<17)	/* Fill the payload with a pattern */
+#define F_TCP           (1<<18)	/* Send TCP packet instead of UDP */
 
 /* Thread control flag bits */
 #define T_STOP        (1<<0)	/* Stop run */
@@ -664,6 +666,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 	if (pkt_dev->flags & F_PATTERN)
 		seq_puts(seq, "PATTERN  ");
 
+	if (pkt_dev->flags & F_TCP)
+		seq_puts(seq, "TCP  ");
+
 	if (pkt_dev->flags & F_MPLS_RND)
 		seq_puts(seq,  "MPLS_RND  ");
 
@@ -1342,6 +1347,12 @@ static ssize_t pktgen_if_write(struct file *file,
 		else if (strcmp(f, "!PATTERN") == 0)
 			pkt_dev->flags &= ~F_PATTERN;
 
+		else if (strcmp(f, "TCP") == 0)
+			pkt_dev->flags |= F_TCP;
+
+		else if (strcmp(f, "!TCP") == 0)
+			pkt_dev->flags &= ~F_TCP;
+
 		else {
 			sprintf(pg_result,
 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
@@ -2950,12 +2961,39 @@ static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
 	return skb;
 }
 
+static int fill_packet_tcp(struct pktgen_dev *pkt_dev, struct sk_buff *skb)
+{
+	struct tcphdr *tcph =
+		(struct tcphdr *)skb_put(skb, sizeof(struct tcphdr));
+
+	memset(tcph, 0, sizeof(*tcph));
+	tcph->source = htons(pkt_dev->cur_udp_src);
+	tcph->dest = htons(pkt_dev->cur_udp_dst);
+	tcph->doff = sizeof(struct tcphdr) >> 2;
+	return pkt_dev->cur_pkt_size - ETH_HLEN - 20 - sizeof(struct tcphdr) -
+		pkt_dev->pkt_overhead;
+}
+
+static int fill_packet_udp(struct pktgen_dev *pkt_dev, struct sk_buff *skb)
+{
+	struct udphdr *udph =
+		(struct udphdr *)skb_put(skb, sizeof(struct udphdr));
+	int datalen = pkt_dev->cur_pkt_size - ETH_HLEN - 20 -
+		      sizeof(struct udphdr) - pkt_dev->pkt_overhead;
+
+	udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
+	udph->source = htons(pkt_dev->cur_udp_src);
+	udph->dest = htons(pkt_dev->cur_udp_dst);
+	udph->len = htons(datalen + sizeof(struct udphdr));
+	udph->check = 0;
+	return datalen;
+}
+
 static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 					struct pktgen_dev *pkt_dev)
 {
 	struct sk_buff *skb = NULL;
 	__u8 *eth;
-	struct udphdr *udph;
 	int datalen, iplen;
 	struct iphdr *iph;
 	__be16 protocol = htons(ETH_P_IP);
@@ -3017,29 +3055,27 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 	iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
 
 	skb_set_transport_header(skb, skb->len);
-	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
+
+	if (pkt_dev->flags & F_TCP)
+		datalen = fill_packet_tcp(pkt_dev, skb);
+	else
+		datalen = fill_packet_udp(pkt_dev, skb);
+
 	skb_set_queue_mapping(skb, queue_map);
 	skb->priority = pkt_dev->skb_priority;
 
 	memcpy(eth, pkt_dev->hh, 12);
 	*(__be16 *) & eth[12] = protocol;
 
-	/* Eth + IPh + UDPh + mpls */
-	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
-		  pkt_dev->pkt_overhead;
 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
 		datalen = sizeof(struct pktgen_hdr);
 
-	udph->source = htons(pkt_dev->cur_udp_src);
-	udph->dest = htons(pkt_dev->cur_udp_dst);
-	udph->len = htons(datalen + 8);	/* DATA + udphdr */
-	udph->check = 0;
 
 	iph->ihl = 5;
 	iph->version = 4;
 	iph->ttl = 32;
 	iph->tos = pkt_dev->tos;
-	iph->protocol = IPPROTO_UDP;	/* UDP */
+	iph->protocol = pkt_dev->flags & F_TCP ? IPPROTO_TCP : IPPROTO_UDP;
 	iph->saddr = pkt_dev->cur_saddr;
 	iph->daddr = pkt_dev->cur_daddr;
 	iph->id = htons(pkt_dev->ip_id);
@@ -3055,10 +3091,17 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 	if (!(pkt_dev->flags & F_UDPCSUM)) {
 		skb->ip_summed = CHECKSUM_NONE;
 	} else if (odev->features & NETIF_F_V4_CSUM) {
-		skb->ip_summed = CHECKSUM_PARTIAL;
-		skb->csum = 0;
-		udp4_hwcsum(skb, udph->source, udph->dest);
+		skb_checksum_setup(skb, true);
+	} else if (pkt_dev->flags & F_TCP) {
+		struct tcphdr *tcph = tcp_hdr(skb);
+
+		skb->ip_summed = CHECKSUM_NONE;
+		tcph->check = tcp_v4_check(skb->len, iph->saddr, iph->daddr,
+					   csum_partial(tcph, tcph->doff << 2,
+							skb->csum));
 	} else {
+		struct udphdr * const udph =
+			(struct udphdr *)skb_transport_header(skb);
 		__wsum csum = udp_csum(skb);
 
 		/* add protocol-dependent pseudo-header */
@@ -3072,6 +3115,20 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 
 	pktgen_finalize_skb(pkt_dev, skb, datalen);
 
+	if (odev->mtu < skb->len) {
+		int hdrlen = skb_transport_header(skb) - skb_mac_header(skb);
+
+		if (pkt_dev->flags & F_TCP) {
+			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+			hdrlen += tcp_hdrlen(skb);
+		} else {
+			skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+			hdrlen += sizeof(struct udphdr);
+		}
+		skb_shinfo(skb)->gso_size = odev->mtu - hdrlen;
+		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len - hdrlen, skb_shinfo(skb)->gso_size);
+	}
+
 #ifdef CONFIG_XFRM
 	if (!process_ipsec(pkt_dev, skb, protocol))
 		return NULL;
@@ -3559,6 +3616,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
 		pkt_dev->last_pkt_size = pkt_dev->skb->len;
 		pkt_dev->allocated_skbs++;
 		pkt_dev->clone_count = 0;	/* reset counter */
+
+		if (netif_needs_gso(pkt_dev->skb, netif_skb_features(pkt_dev->skb))) {
+			pr_err("Device don't have necessary GSO features! netif_skb_features: %llX summed %u\n",
+			       netif_skb_features(pkt_dev->skb),
+			       pkt_dev->skb->ip_summed);
+			pkt_dev->sofar++;
+			goto out;
+		}
 	}
 
 	if (pkt_dev->delay && pkt_dev->last_ok)
@@ -3608,7 +3673,7 @@ unlock:
 	HARD_TX_UNLOCK(odev, txq);
 
 	local_bh_enable();
-
+out:
 	/* If pkt_dev->count is zero, then run forever */
 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
 		pktgen_wait_for_skb(pkt_dev);

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets
  2014-08-04 13:37 ` [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets Zoltan Kiss
  2014-08-05 20:09   ` David Miller
@ 2014-08-05 20:09   ` David Miller
  2014-08-06 18:09     ` Zoltan Kiss
  2014-08-06 18:09     ` Zoltan Kiss
  1 sibling, 2 replies; 15+ messages in thread
From: David Miller @ 2014-08-05 20:09 UTC (permalink / raw)
  To: zoltan.kiss
  Cc: steffen.klassert, minipli, dborkman, tgraf, joe, netdev,
	linux-kernel, xen-devel

From: Zoltan Kiss <zoltan.kiss@citrix.com>
Date: Mon, 4 Aug 2014 14:37:14 +0100

> @@ -3559,6 +3616,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
>  		pkt_dev->last_pkt_size = pkt_dev->skb->len;
>  		pkt_dev->allocated_skbs++;
>  		pkt_dev->clone_count = 0;	/* reset counter */
> +
> +		if (netif_needs_gso(pkt_dev->skb, netif_skb_features(pkt_dev->skb))) {
> +			pr_err("Device don't have necessary GSO features! netif_skb_features: %llX summed %u\n",
> +			       netif_skb_features(pkt_dev->skb),
> +			       pkt_dev->skb->ip_summed);
> +			pkt_dev->sofar++;
> +			goto out;
> +		}
>  	}
>  
>  	if (pkt_dev->delay && pkt_dev->last_ok)

Woe be to the person who actually triggers this condition.  This function
happens potentially millions of times per second, there is no value in
emitting a log message every time.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets
  2014-08-04 13:37 ` [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets Zoltan Kiss
@ 2014-08-05 20:09   ` David Miller
  2014-08-05 20:09   ` David Miller
  1 sibling, 0 replies; 15+ messages in thread
From: David Miller @ 2014-08-05 20:09 UTC (permalink / raw)
  To: zoltan.kiss
  Cc: steffen.klassert, netdev, linux-kernel, joe, dborkman, tgraf,
	xen-devel, minipli

From: Zoltan Kiss <zoltan.kiss@citrix.com>
Date: Mon, 4 Aug 2014 14:37:14 +0100

> @@ -3559,6 +3616,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
>  		pkt_dev->last_pkt_size = pkt_dev->skb->len;
>  		pkt_dev->allocated_skbs++;
>  		pkt_dev->clone_count = 0;	/* reset counter */
> +
> +		if (netif_needs_gso(pkt_dev->skb, netif_skb_features(pkt_dev->skb))) {
> +			pr_err("Device don't have necessary GSO features! netif_skb_features: %llX summed %u\n",
> +			       netif_skb_features(pkt_dev->skb),
> +			       pkt_dev->skb->ip_summed);
> +			pkt_dev->sofar++;
> +			goto out;
> +		}
>  	}
>  
>  	if (pkt_dev->delay && pkt_dev->last_ok)

Woe be to the person who actually triggers this condition.  This function
happens potentially millions of times per second, there is no value in
emitting a log message every time.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets
  2014-08-05 20:09   ` David Miller
  2014-08-06 18:09     ` Zoltan Kiss
@ 2014-08-06 18:09     ` Zoltan Kiss
  1 sibling, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-06 18:09 UTC (permalink / raw)
  To: David Miller
  Cc: steffen.klassert, minipli, dborkman, tgraf, joe, netdev,
	linux-kernel, xen-devel

On 05/08/14 21:09, David Miller wrote:
> From: Zoltan Kiss <zoltan.kiss@citrix.com>
> Date: Mon, 4 Aug 2014 14:37:14 +0100
>
>> @@ -3559,6 +3616,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
>>   		pkt_dev->last_pkt_size = pkt_dev->skb->len;
>>   		pkt_dev->allocated_skbs++;
>>   		pkt_dev->clone_count = 0;	/* reset counter */
>> +
>> +		if (netif_needs_gso(pkt_dev->skb, netif_skb_features(pkt_dev->skb))) {
>> +			pr_err("Device don't have necessary GSO features! netif_skb_features: %llX summed %u\n",
>> +			       netif_skb_features(pkt_dev->skb),
>> +			       pkt_dev->skb->ip_summed);
>> +			pkt_dev->sofar++;
>> +			goto out;
>> +		}
>>   	}
>>
>>   	if (pkt_dev->delay && pkt_dev->last_ok)
>
> Woe be to the person who actually triggers this condition.  This function
> happens potentially millions of times per second, there is no value in
> emitting a log message every time.
Indeed, I'll put a ratelimit around it.

Zoli


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets
  2014-08-05 20:09   ` David Miller
@ 2014-08-06 18:09     ` Zoltan Kiss
  2014-08-06 18:09     ` Zoltan Kiss
  1 sibling, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-08-06 18:09 UTC (permalink / raw)
  To: David Miller
  Cc: steffen.klassert, netdev, linux-kernel, joe, dborkman, tgraf,
	xen-devel, minipli

On 05/08/14 21:09, David Miller wrote:
> From: Zoltan Kiss <zoltan.kiss@citrix.com>
> Date: Mon, 4 Aug 2014 14:37:14 +0100
>
>> @@ -3559,6 +3616,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
>>   		pkt_dev->last_pkt_size = pkt_dev->skb->len;
>>   		pkt_dev->allocated_skbs++;
>>   		pkt_dev->clone_count = 0;	/* reset counter */
>> +
>> +		if (netif_needs_gso(pkt_dev->skb, netif_skb_features(pkt_dev->skb))) {
>> +			pr_err("Device don't have necessary GSO features! netif_skb_features: %llX summed %u\n",
>> +			       netif_skb_features(pkt_dev->skb),
>> +			       pkt_dev->skb->ip_summed);
>> +			pkt_dev->sofar++;
>> +			goto out;
>> +		}
>>   	}
>>
>>   	if (pkt_dev->delay && pkt_dev->last_ok)
>
> Woe be to the person who actually triggers this condition.  This function
> happens potentially millions of times per second, there is no value in
> emitting a log message every time.
Indeed, I'll put a ratelimit around it.

Zoli

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually
  2014-07-30 16:20 [PATCH net-next v3 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
  2014-07-30 16:20 ` [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually Zoltan Kiss
@ 2014-07-30 16:20 ` Zoltan Kiss
  1 sibling, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-07-30 16:20 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: Zoltan Kiss, David S. Miller, Thomas Graf, Joe Perches, netdev,
	linux-kernel, xen-devel

By defining the number of frags via "nfrags", their sizes get calculated by
pktgen. This patch allows their offsets and sizes to be specified via
"frag_off-len", in a comma separated list (e.g.
"frag_off-len 0-1,500-200,5000-10,9-100). The first is the offset
(0 <= offset < 2^16), second is size (0 < length <= 65536). This also determines
the number of frags, so it overwrites "frags" (and vice versa, "frags"
invalidate this setting)
xen-netback is prone to have problem with compound pages, as the memory granting
interface can only handle 4k pages. This extension of pktgen is proven to be
useful to test that.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v2: allocate new compound page only if order is smaller

v3: fix up indexing and reseting of pages array

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ab30856..a04d9d4 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -236,6 +236,11 @@ struct flow_state {
 /* flow flag bits */
 #define F_INIT   (1<<0)		/* flow has been initialized */
 
+struct frag_param {
+	int offset;
+	int length;
+};
+
 struct pktgen_dev {
 	/*
 	 * Try to keep frequent/infrequent used vars. separated.
@@ -258,6 +263,11 @@ struct pktgen_dev {
 	int max_pkt_size;
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
+	/* offset-length pairs for the frags, only the first nfrags are valid,
+	 * and only if frags[0].length != 0, as zero frag size is not valid
+	 * (and neither negative)
+	 */
+	struct frag_param frags[MAX_SKB_FRAGS];
 	struct page *pages[MAX_SKB_FRAGS];
 	u64 delay;		/* nano-seconds */
 
@@ -541,6 +551,18 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
 		   pkt_dev->clone_skb, pkt_dev->odevname);
 
+	if (pkt_dev->frags[0].length) {
+		int n;
+
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			seq_printf(seq, "     %d. offset: %d length: %d%s\n", n,
+				   pkt_dev->frags[n].offset,
+				   pkt_dev->frags[n].length,
+				   n == pkt_dev->nfrags-1 ? "" : ",");
+	} else {
+			seq_puts(seq, "     No frag parameters defined\n");
+	}
+
 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
 		   pkt_dev->lflow);
 
@@ -845,6 +867,50 @@ static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
 	return i;
 }
 
+static ssize_t get_sizes(const char __user *buffer, struct pktgen_dev *pkt_dev)
+{
+	unsigned int n = 0;
+	char c;
+	ssize_t i = 0;
+	int len;
+
+	pkt_dev->nfrags = 0;
+	do {
+		unsigned long tmp;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		/* Maximum skb size minus 1 */
+		if (tmp >= 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].offset = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		if (c != '-')
+			return -EINVAL;
+		i++;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		if (tmp < 1 || tmp > 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].length = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		i++;
+		n++;
+		if (n > MAX_SKB_FRAGS)
+			return -E2BIG;
+	} while (c == ',');
+
+	pkt_dev->nfrags = n;
+	return i;
+}
+
 static ssize_t pktgen_if_write(struct file *file,
 			       const char __user * user_buffer, size_t count,
 			       loff_t * offset)
@@ -972,9 +1038,30 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		i += len;
 		pkt_dev->nfrags = value;
+		/* Invalidate whatever was specified before */
+		pkt_dev->frags[0].length = 0;
 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
 		return count;
 	}
+
+	if (!strcmp(name, "frag_off-len")) {
+		unsigned int n, cnt;
+
+		len = get_sizes(&user_buffer[i], pkt_dev);
+		if (len < 0)
+			return len;
+		i += len;
+		cnt = sprintf(pg_result, "OK: frags=%u frag offsets-sizes=",
+			      pkt_dev->nfrags);
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			cnt += sprintf(pg_result + cnt, "%d-%d%s",
+				       pkt_dev->frags[n].offset,
+				       pkt_dev->frags[n].length,
+				       n == pkt_dev->nfrags-1 ? "" : ",");
+
+		return count;
+	}
+
 	if (!strcmp(name, "delay")) {
 		len = num_arg(&user_buffer[i], 10, &value);
 		if (len < 0)
@@ -2734,12 +2821,25 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;
-		int frag_len;
+		int frag_len = 0;
 
 
 		if (frags > MAX_SKB_FRAGS)
 			frags = MAX_SKB_FRAGS;
-		len = datalen - frags * PAGE_SIZE;
+
+		if (pkt_dev->frags[0].length) {
+			for (i = 0; i < frags; ++i)
+				frag_len += pkt_dev->frags[i].length;
+			if (frag_len > datalen) {
+				pr_err("Payload length (%d) smaller than frags (%d)\n",
+				       datalen, frag_len);
+				return;
+			}
+		} else {
+			frag_len = frags * PAGE_SIZE;
+		}
+
+		len = datalen - frag_len;
 		if (len > 0) {
 			if (pkt_dev->flags & F_PATTERN)
 				offset = pattern_to_packet(skb_put(skb, len),
@@ -2747,28 +2847,44 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 							   &incomplete);
 			else
 				memset(skb_put(skb, len), 0, len);
-			datalen = frags * PAGE_SIZE;
+			datalen = frag_len;
 		}
 
 		i = 0;
 		frag_len = (datalen/frags) < PAGE_SIZE ?
 			   (datalen/frags) : PAGE_SIZE;
 		while (datalen > 0) {
-			int fragpage;
+			int fragpage, order = 0;
 			gfp_t flags = GFP_KERNEL | __GFP_ZERO;
 
-			if (pkt_dev->flags & F_PATTERN)
+			if (pkt_dev->flags & F_PATTERN ||
+			    pkt_dev->frags[0].length)
 				fragpage = i;
 			else
 				fragpage = 0;
 
+			/* Free this page if we gonna need a different order */
+			if (pkt_dev->frags[0].length) {
+				int max_off = pkt_dev->frags[i].offset +
+					      pkt_dev->frags[i].length;
+				order = get_order(max_off);
+				if (pkt_dev->pages[fragpage] &&
+				    order != compound_order(pkt_dev->pages[fragpage])) {
+					if (pkt_dev->pages[fragpage]) {
+						put_page(pkt_dev->pages[fragpage]);
+						pkt_dev->pages[fragpage] = NULL;
+					}
+				}
+			}
+
 			if (unlikely(!pkt_dev->pages[fragpage])) {
 				int node = numa_node_id();
 
 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
 					node = pkt_dev->node;
+				order ? flags |= __GFP_COMP : 0;
 				pkt_dev->pages[fragpage] =
-					alloc_pages_node(node, flags, 0);
+					alloc_pages_node(node, flags, order);
 				if (!pkt_dev->pages[fragpage])
 					break;
 			}
@@ -2776,8 +2892,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			skb_frag_set_page(skb, i, pkt_dev->pages[fragpage]);
 
 			skb_shinfo(skb)->frags[i].page_offset = 0;
+			if (pkt_dev->frags[0].length) {
+				skb_shinfo(skb)->frags[i].page_offset =
+					pkt_dev->frags[i].offset;
+				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
+						  pkt_dev->frags[i].length);
+			}
 			/*last fragment, fill rest of data*/
-			if (i == (frags - 1))
+			else if (i == (frags - 1))
 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
 			else
@@ -3826,8 +3948,10 @@ static int pktgen_remove_device(struct pktgen_thread *t,
 #endif
 	vfree(pkt_dev->flows);
 	for (i = 0; i < MAX_SKB_FRAGS; ++i)
-		if (pkt_dev->pages[i])
+		if (pkt_dev->pages[i]) {
 			put_page(pkt_dev->pages[i]);
+			pkt_dev->pages[i] = NULL;
+		}
 	kfree_rcu(pkt_dev, rcu);
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually
  2014-07-30 16:20 [PATCH net-next v3 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
@ 2014-07-30 16:20 ` Zoltan Kiss
  2014-07-30 16:20 ` Zoltan Kiss
  1 sibling, 0 replies; 15+ messages in thread
From: Zoltan Kiss @ 2014-07-30 16:20 UTC (permalink / raw)
  To: Steffen Klassert, Mathias Krause, Daniel Borkmann
  Cc: netdev, linux-kernel, Joe Perches, Zoltan Kiss, Thomas Graf,
	xen-devel, David S. Miller

By defining the number of frags via "nfrags", their sizes get calculated by
pktgen. This patch allows their offsets and sizes to be specified via
"frag_off-len", in a comma separated list (e.g.
"frag_off-len 0-1,500-200,5000-10,9-100). The first is the offset
(0 <= offset < 2^16), second is size (0 < length <= 65536). This also determines
the number of frags, so it overwrites "frags" (and vice versa, "frags"
invalidate this setting)
xen-netback is prone to have problem with compound pages, as the memory granting
interface can only handle 4k pages. This extension of pktgen is proven to be
useful to test that.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Joe Perches <joe@perches.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
---
v2: allocate new compound page only if order is smaller

v3: fix up indexing and reseting of pages array

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ab30856..a04d9d4 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -236,6 +236,11 @@ struct flow_state {
 /* flow flag bits */
 #define F_INIT   (1<<0)		/* flow has been initialized */
 
+struct frag_param {
+	int offset;
+	int length;
+};
+
 struct pktgen_dev {
 	/*
 	 * Try to keep frequent/infrequent used vars. separated.
@@ -258,6 +263,11 @@ struct pktgen_dev {
 	int max_pkt_size;
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
+	/* offset-length pairs for the frags, only the first nfrags are valid,
+	 * and only if frags[0].length != 0, as zero frag size is not valid
+	 * (and neither negative)
+	 */
+	struct frag_param frags[MAX_SKB_FRAGS];
 	struct page *pages[MAX_SKB_FRAGS];
 	u64 delay;		/* nano-seconds */
 
@@ -541,6 +551,18 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
 		   pkt_dev->clone_skb, pkt_dev->odevname);
 
+	if (pkt_dev->frags[0].length) {
+		int n;
+
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			seq_printf(seq, "     %d. offset: %d length: %d%s\n", n,
+				   pkt_dev->frags[n].offset,
+				   pkt_dev->frags[n].length,
+				   n == pkt_dev->nfrags-1 ? "" : ",");
+	} else {
+			seq_puts(seq, "     No frag parameters defined\n");
+	}
+
 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
 		   pkt_dev->lflow);
 
@@ -845,6 +867,50 @@ static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
 	return i;
 }
 
+static ssize_t get_sizes(const char __user *buffer, struct pktgen_dev *pkt_dev)
+{
+	unsigned int n = 0;
+	char c;
+	ssize_t i = 0;
+	int len;
+
+	pkt_dev->nfrags = 0;
+	do {
+		unsigned long tmp;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		/* Maximum skb size minus 1 */
+		if (tmp >= 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].offset = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		if (c != '-')
+			return -EINVAL;
+		i++;
+
+		len = num_arg(&buffer[i], 5, &tmp);
+		if (len <= 0)
+			return len;
+		if (tmp < 1 || tmp > 65536)
+			return -EINVAL;
+		pkt_dev->frags[n].length = tmp;
+		i += len;
+		if (get_user(c, &buffer[i]))
+			return -EFAULT;
+		i++;
+		n++;
+		if (n > MAX_SKB_FRAGS)
+			return -E2BIG;
+	} while (c == ',');
+
+	pkt_dev->nfrags = n;
+	return i;
+}
+
 static ssize_t pktgen_if_write(struct file *file,
 			       const char __user * user_buffer, size_t count,
 			       loff_t * offset)
@@ -972,9 +1038,30 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		i += len;
 		pkt_dev->nfrags = value;
+		/* Invalidate whatever was specified before */
+		pkt_dev->frags[0].length = 0;
 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
 		return count;
 	}
+
+	if (!strcmp(name, "frag_off-len")) {
+		unsigned int n, cnt;
+
+		len = get_sizes(&user_buffer[i], pkt_dev);
+		if (len < 0)
+			return len;
+		i += len;
+		cnt = sprintf(pg_result, "OK: frags=%u frag offsets-sizes=",
+			      pkt_dev->nfrags);
+		for (n = 0; n < pkt_dev->nfrags; n++)
+			cnt += sprintf(pg_result + cnt, "%d-%d%s",
+				       pkt_dev->frags[n].offset,
+				       pkt_dev->frags[n].length,
+				       n == pkt_dev->nfrags-1 ? "" : ",");
+
+		return count;
+	}
+
 	if (!strcmp(name, "delay")) {
 		len = num_arg(&user_buffer[i], 10, &value);
 		if (len < 0)
@@ -2734,12 +2821,25 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;
-		int frag_len;
+		int frag_len = 0;
 
 
 		if (frags > MAX_SKB_FRAGS)
 			frags = MAX_SKB_FRAGS;
-		len = datalen - frags * PAGE_SIZE;
+
+		if (pkt_dev->frags[0].length) {
+			for (i = 0; i < frags; ++i)
+				frag_len += pkt_dev->frags[i].length;
+			if (frag_len > datalen) {
+				pr_err("Payload length (%d) smaller than frags (%d)\n",
+				       datalen, frag_len);
+				return;
+			}
+		} else {
+			frag_len = frags * PAGE_SIZE;
+		}
+
+		len = datalen - frag_len;
 		if (len > 0) {
 			if (pkt_dev->flags & F_PATTERN)
 				offset = pattern_to_packet(skb_put(skb, len),
@@ -2747,28 +2847,44 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 							   &incomplete);
 			else
 				memset(skb_put(skb, len), 0, len);
-			datalen = frags * PAGE_SIZE;
+			datalen = frag_len;
 		}
 
 		i = 0;
 		frag_len = (datalen/frags) < PAGE_SIZE ?
 			   (datalen/frags) : PAGE_SIZE;
 		while (datalen > 0) {
-			int fragpage;
+			int fragpage, order = 0;
 			gfp_t flags = GFP_KERNEL | __GFP_ZERO;
 
-			if (pkt_dev->flags & F_PATTERN)
+			if (pkt_dev->flags & F_PATTERN ||
+			    pkt_dev->frags[0].length)
 				fragpage = i;
 			else
 				fragpage = 0;
 
+			/* Free this page if we gonna need a different order */
+			if (pkt_dev->frags[0].length) {
+				int max_off = pkt_dev->frags[i].offset +
+					      pkt_dev->frags[i].length;
+				order = get_order(max_off);
+				if (pkt_dev->pages[fragpage] &&
+				    order != compound_order(pkt_dev->pages[fragpage])) {
+					if (pkt_dev->pages[fragpage]) {
+						put_page(pkt_dev->pages[fragpage]);
+						pkt_dev->pages[fragpage] = NULL;
+					}
+				}
+			}
+
 			if (unlikely(!pkt_dev->pages[fragpage])) {
 				int node = numa_node_id();
 
 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
 					node = pkt_dev->node;
+				order ? flags |= __GFP_COMP : 0;
 				pkt_dev->pages[fragpage] =
-					alloc_pages_node(node, flags, 0);
+					alloc_pages_node(node, flags, order);
 				if (!pkt_dev->pages[fragpage])
 					break;
 			}
@@ -2776,8 +2892,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 			skb_frag_set_page(skb, i, pkt_dev->pages[fragpage]);
 
 			skb_shinfo(skb)->frags[i].page_offset = 0;
+			if (pkt_dev->frags[0].length) {
+				skb_shinfo(skb)->frags[i].page_offset =
+					pkt_dev->frags[i].offset;
+				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
+						  pkt_dev->frags[i].length);
+			}
 			/*last fragment, fill rest of data*/
-			if (i == (frags - 1))
+			else if (i == (frags - 1))
 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
 			else
@@ -3826,8 +3948,10 @@ static int pktgen_remove_device(struct pktgen_thread *t,
 #endif
 	vfree(pkt_dev->flows);
 	for (i = 0; i < MAX_SKB_FRAGS; ++i)
-		if (pkt_dev->pages[i])
+		if (pkt_dev->pages[i]) {
 			put_page(pkt_dev->pages[i]);
+			pkt_dev->pages[i] = NULL;
+		}
 	kfree_rcu(pkt_dev, rcu);
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2014-08-06 18:09 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-04 13:37 [PATCH net-next v4 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
2014-08-04 13:37 ` [PATCH net-next 1/4 v5] pktgen: Fill the payload optionally with a pattern Zoltan Kiss
2014-08-04 13:37 ` Zoltan Kiss
2014-08-04 13:37 ` [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually Zoltan Kiss
2014-08-04 13:37 ` Zoltan Kiss
2014-08-04 13:37 ` [PATCH net-next 3/4 RFC] pktgen: Fixing UPD checksum calculation Zoltan Kiss
2014-08-04 13:37 ` Zoltan Kiss
2014-08-04 13:37 ` [PATCH net-next v4 4/4 RFC] pktgen: Allow sending IPv4 TCP packets Zoltan Kiss
2014-08-05 20:09   ` David Miller
2014-08-05 20:09   ` David Miller
2014-08-06 18:09     ` Zoltan Kiss
2014-08-06 18:09     ` Zoltan Kiss
2014-08-04 13:37 ` Zoltan Kiss
  -- strict thread matches above, loose matches on Subject: below --
2014-07-30 16:20 [PATCH net-next v3 0/3] pktgen: Upstreaming features useful for xen-netback/front testing Zoltan Kiss
2014-07-30 16:20 ` [PATCH net-next v3 2/4] pktgen: Allow setting frag sizes individually Zoltan Kiss
2014-07-30 16:20 ` Zoltan Kiss

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.