All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] app/testpmd: support runt and ultra-small frames in txonly
@ 2026-07-06 13:20 Xingui Yang
  2026-07-22 22:22 ` Stephen Hemminger
  0 siblings, 1 reply; 2+ messages in thread
From: Xingui Yang @ 2026-07-06 13:20 UTC (permalink / raw)
  To: dev
  Cc: stephen, david.marchand, aman.deep.singh, fengchengwen, lihuisong,
	liuyonglong, kangfenglong

Allow set txpkts to accept packet lengths as small as one byte,
enabling generation of runt frames and ultra-small packets for
testing NIC padding logic and undersized frame handling.

The txonly engine handles three ranges: normal (>= 42 bytes) with
full Ethernet/IPv4/UDP headers, runt (14-41 bytes) with truncated
headers, and ultra-small (< 14 bytes) filled with a repeating
pattern. Checksum offloads are disabled when headers are incomplete.

Minimum length validation in set_tx_pkt_segments() is relaxed from
42 to 1 byte. Random split and multi-flow still require the full
header stack in the first segment, as enforced by tx_only_begin().

Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Suggested-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test-pmd/config.c                       | 18 +++--
 app/test-pmd/txonly.c                       | 79 ++++++++++++++++++---
 doc/guides/rel_notes/release_26_07.rst      |  5 +-
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 21 ++++++
 4 files changed, 104 insertions(+), 19 deletions(-)

diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 3df7412ef6..31b33d3956 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -6327,9 +6327,9 @@ set_tx_pkt_segments(unsigned int *seg_lengths, unsigned int nb_segs)
 	/*
 	 * Check that each segment length is greater or equal than
 	 * the mbuf data size.
-	 * Check also that the total packet length is greater or equal than the
-	 * size of an empty UDP/IP packet (sizeof(struct rte_ether_hdr) +
-	 * 20 + 8).
+	 * The total packet length may be as small as one byte, this allows
+	 * generating runt frames and ultra-small packets for testing NIC
+	 * padding logic and undersized frame handling.
 	 */
 	tx_pkt_len = 0;
 	for (i = 0; i < nb_segs; i++) {
@@ -6341,12 +6341,16 @@ set_tx_pkt_segments(unsigned int *seg_lengths, unsigned int nb_segs)
 		}
 		tx_pkt_len = (uint16_t)(tx_pkt_len + seg_lengths[i]);
 	}
-	if (tx_pkt_len < (sizeof(struct rte_ether_hdr) + 20 + 8)) {
-		fprintf(stderr, "total packet length=%u < %d - give up\n",
-				(unsigned) tx_pkt_len,
-				(int)(sizeof(struct rte_ether_hdr) + 20 + 8));
+	if (tx_pkt_len == 0) {
+		fprintf(stderr, "total packet length is zero - give up\n");
 		return;
 	}
+	if (tx_pkt_len < (sizeof(struct rte_ether_hdr) + 20 + 8))
+		fprintf(stderr,
+				"Warning: total packet length=%u < %d (full Ether/IP/UDP), "
+				"generating runt or ultra-small frames\n",
+				(unsigned int) tx_pkt_len,
+				(int)(sizeof(struct rte_ether_hdr) + 20 + 8));
 
 	for (i = 0; i < nb_segs; i++)
 		tx_pkt_seg_lengths[i] = (uint16_t) seg_lengths[i];
diff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c
index a4acb85d29..6e102e2447 100644
--- a/app/test-pmd/txonly.c
+++ b/app/test-pmd/txonly.c
@@ -64,6 +64,9 @@ static int32_t timestamp_off; /**< Timestamp dynamic field offset */
 static bool timestamp_enable; /**< Timestamp enable */
 static uint64_t timestamp_initial[RTE_MAX_ETHPORTS];
 
+/* Fill pattern for ultra-small packets too small to hold Ethernet header. */
+static const char pad_pattern[] = "0123456789abcdef";
+
 static void
 copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt,
 		     unsigned offset)
@@ -76,6 +79,12 @@ copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt,
 	while (offset >= seg->data_len) {
 		offset -= seg->data_len;
 		seg = seg->next;
+		/*
+		 * The packet may be shorter than the header stack when
+		 * generating runt frames; stop once it runs out of segments.
+		 */
+		if (seg == NULL)
+			return;
 	}
 	copy_len = seg->data_len - offset;
 	seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);
@@ -84,6 +93,8 @@ copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt,
 		len -= copy_len;
 		buf = ((char*) buf + copy_len);
 		seg = seg->next;
+		if (seg == NULL)
+			return;
 		seg_buf = rte_pktmbuf_mtod(seg, char *);
 		copy_len = seg->data_len;
 	}
@@ -192,9 +203,6 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp,
 	pkt->ol_flags |= ol_flags;
 	pkt->vlan_tci = vlan_tci;
 	pkt->vlan_tci_outer = vlan_tci_outer;
-	pkt->l2_len = sizeof(struct rte_ether_hdr);
-	pkt->l3_len = sizeof(struct rte_ipv4_hdr);
-
 	pkt_len = pkt->data_len;
 	pkt_seg = pkt;
 	for (i = 1; i < nb_segs; i++) {
@@ -204,15 +212,60 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp,
 		pkt_len += pkt_seg->data_len;
 	}
 	pkt_seg->next = NULL; /* Last segment of packet. */
+
 	/*
 	 * Copy headers in first packet segment(s).
+	 * For runt frames (pkt_len < full header stack), headers are
+	 * truncated to fit, copy_buf_to_pkt_segs stops at the last
+	 * segment so no out-of-bounds access occurs. For ultra-small
+	 * packets (pkt_len < Ethernet header), a fill pattern is used
+	 * instead of headers.
 	 */
-	copy_buf_to_pkt(eth_hdr, sizeof(*eth_hdr), pkt, 0);
-	copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt,
-			sizeof(struct rte_ether_hdr));
-	copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt,
-			sizeof(struct rte_ether_hdr) +
-			sizeof(struct rte_ipv4_hdr));
+	if (pkt_len >= sizeof(struct rte_ether_hdr)) {
+		/*
+		 * A runt frame may be too short to carry a full IPv4/UDP
+		 * header. Clamp l3_len and drop any checksum offload whose
+		 * header is not fully present, so the PMD is never asked to
+		 * checksum bytes that are not in the frame. pkt_len is at
+		 * least sizeof(rte_ether_hdr), so the subtraction below
+		 * cannot underflow.
+		 */
+		pkt->l2_len = sizeof(struct rte_ether_hdr);
+		pkt->l3_len = RTE_MIN(sizeof(struct rte_ipv4_hdr),
+				pkt_len - sizeof(struct rte_ether_hdr));
+		if (pkt_len < sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv4_hdr))
+			pkt->ol_flags &= ~(RTE_MBUF_F_TX_IP_CKSUM |
+					RTE_MBUF_F_TX_L4_MASK);
+		else if (pkt_len < sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv4_hdr) +
+				sizeof(struct rte_udp_hdr))
+			pkt->ol_flags &= ~RTE_MBUF_F_TX_L4_MASK;
+		copy_buf_to_pkt(eth_hdr, sizeof(*eth_hdr), pkt, 0);
+		copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt,
+				sizeof(struct rte_ether_hdr));
+		copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt,
+				sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv4_hdr));
+	} else {
+		struct rte_mbuf *seg;
+		uint32_t j;
+
+		pkt->l2_len = 0;
+		pkt->l3_len = 0;
+		pkt->ol_flags &= ~(RTE_MBUF_F_TX_IP_CKSUM |
+				RTE_MBUF_F_TX_L4_MASK);
+		seg = pkt;
+		while (seg != NULL) {
+			char *data = rte_pktmbuf_mtod(seg, char *);
+
+			for (j = 0; j < seg->data_len; j++)
+				data[j] = pad_pattern[j %
+					(sizeof(pad_pattern) - 1)];
+			seg = seg->next;
+		}
+	}
+
 	if (txonly_multi_flow) {
 		uint16_t src_var = RTE_PER_LCORE(_src_port_var);
 		struct rte_udp_hdr *udp_hdr;
@@ -405,7 +458,13 @@ tx_only_begin(portid_t pi)
 	pkt_hdr_len = (uint16_t)(sizeof(struct rte_ether_hdr) +
 				 sizeof(struct rte_ipv4_hdr) +
 				 sizeof(struct rte_udp_hdr));
-	pkt_data_len = tx_pkt_length - pkt_hdr_len;
+	/*
+	 * tx_pkt_length may be smaller than the full header stack when
+	 * generating runt or ultra-small frames; clamp the payload length
+	 * to zero in that case so the IP/UDP length fields stay sane.
+	 */
+	pkt_data_len = tx_pkt_length > pkt_hdr_len ?
+			tx_pkt_length - pkt_hdr_len : 0;
 
 	if ((tx_pkt_split == TX_PKT_SPLIT_RND || txonly_multi_flow) &&
 	    tx_pkt_seg_lengths[0] < pkt_hdr_len) {
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 8b1bdada1a..3fca990f78 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -242,8 +242,9 @@ New Features
 
 * **Updated testpmd application.**
 
-  Added support for setting VLAN priority and CFI/DEI bits
-  in ``tx_vlan set``/``tx_qinq set`` commands and ``vlan_tci`` parameter.
+  * Added support for setting VLAN priority and CFI/DEI bits
+    in ``tx_vlan set``/``tx_qinq set`` commands and ``vlan_tci`` parameter.
+  * Added support for runt and ultra-small frames in the txonly mode.
 
 * **Added AI review helpers.**
 
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 364d348372..b69924c5f9 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -876,6 +876,27 @@ Set the length of each segment of the TX-ONLY packets or length of packet for FL
 
 Where x[,y]* represents a CSV list of values, without white space.
 
+The total packet length may be set as small as one byte, which is below
+the size of an empty IPv4/UDP packet. This generates runt frames or
+ultra-small packets, which is useful for testing how a driver handles
+undersized frames and NIC padding logic.
+
+Normal packets (>= 42 bytes)
+   Full Ethernet/IPv4/UDP headers with checksum offloads.
+
+Runt frames (14-41 bytes)
+   Ethernet header with truncated or absent IPv4/UDP header.
+   Checksum offloads are automatically disabled.
+
+Ultra-small packets (< 14 bytes)
+   No standard headers; filled with a repeating pattern.
+   Checksum offloads are automatically disabled.
+
+Note that random split (``set txsplit rand``) and multi-flow
+(``set txonly-flows``) still require the first segment to hold the full
+Ethernet/IPv4/UDP header stack, so they cannot be combined with runt
+or ultra-small lengths.
+
 set txtimes
 ~~~~~~~~~~~
 
-- 
2.43.0


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

* Re: [PATCH v3] app/testpmd: support runt and ultra-small frames in txonly
  2026-07-06 13:20 [PATCH v3] app/testpmd: support runt and ultra-small frames in txonly Xingui Yang
@ 2026-07-22 22:22 ` Stephen Hemminger
  0 siblings, 0 replies; 2+ messages in thread
From: Stephen Hemminger @ 2026-07-22 22:22 UTC (permalink / raw)
  To: Xingui Yang
  Cc: dev, david.marchand, aman.deep.singh, fengchengwen, lihuisong,
	liuyonglong, kangfenglong

On Mon, 6 Jul 2026 21:20:50 +0800
Xingui Yang <yangxingui@huawei.com> wrote:

> Allow set txpkts to accept packet lengths as small as one byte,
> enabling generation of runt frames and ultra-small packets for
> testing NIC padding logic and undersized frame handling.
> 
> The txonly engine handles three ranges: normal (>= 42 bytes) with
> full Ethernet/IPv4/UDP headers, runt (14-41 bytes) with truncated
> headers, and ultra-small (< 14 bytes) filled with a repeating
> pattern. Checksum offloads are disabled when headers are incomplete.
> 
> Minimum length validation in set_tx_pkt_segments() is relaxed from
> 42 to 1 byte. Random split and multi-flow still require the full
> header stack in the first segment, as enforced by tx_only_begin().
> 
> Signed-off-by: Xingui Yang <yangxingui@huawei.com>
> Suggested-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
 
I would suggest that if NIC ever gets a packet < ethernet header it is
an application bug, and driver should drop it as an transmit error.
Or crash with an assert failed. There is no guarantee that this
will ever work.

Therefore can not see why testpmd needs to do this.

For packets with Ethernet header plus a few more bytes, it makes
sense to allow. There are always control level protocols that are
very short.

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

end of thread, other threads:[~2026-07-22 22:23 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 13:20 [PATCH v3] app/testpmd: support runt and ultra-small frames in txonly Xingui Yang
2026-07-22 22:22 ` Stephen Hemminger

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.