* [PATCH v4] app/testpmd: support runt frames in txonly
@ 2026-08-07 6:17 Xingui Yang
2026-08-09 19:18 ` Stephen Hemminger
0 siblings, 1 reply; 2+ messages in thread
From: Xingui Yang @ 2026-08-07 6:17 UTC (permalink / raw)
To: dev
Cc: stephen, david.marchand, aman.deep.singh, fengchengwen, lihuisong,
liuyonglong, kangfenglong
Allow setting transmit size to be a small value which has Ethernet
header but no IP or UDP header, since control level protocols can be
very short.
Checksum offloads are disabled when headers are incomplete.
copy_buf_to_pkt_segs stops at the last segment to prevent OOB access.
Suggested-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
---
Changes in v4:
- Removed ultra-small frame support (< 14 bytes) per Stephen's review.
---
app/test-pmd/config.c | 13 ++++----
app/test-pmd/txonly.c | 36 +++++++++++++++++++--
doc/guides/rel_notes/release_26_11.rst | 7 ++++
doc/guides/testpmd_app_ug/testpmd_funcs.rst | 11 +++++++
4 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index aa03eb99cc..86c794b923 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -6333,9 +6333,8 @@ 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 must be at least the size of an
+ * Ethernet header.
*/
tx_pkt_len = 0;
for (i = 0; i < nb_segs; i++) {
@@ -6347,10 +6346,10 @@ 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 < sizeof(struct rte_ether_hdr)) {
+ fprintf(stderr, "total packet length=%u < %zu - give up\n",
+ (unsigned int) tx_pkt_len,
+ sizeof(struct rte_ether_hdr));
return;
}
diff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c
index a4acb85d29..e90e28a6e6 100644
--- a/app/test-pmd/txonly.c
+++ b/app/test-pmd/txonly.c
@@ -76,6 +76,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 +90,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;
}
@@ -193,7 +201,6 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp,
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;
@@ -204,6 +211,25 @@ 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. */
+
+ /*
+ * 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(struct rte_ether_hdr),
+ * so the subtraction below cannot underflow.
+ */
+ 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 headers in first packet segment(s).
*/
@@ -405,7 +431,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 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_11.rst b/doc/guides/rel_notes/release_26_11.rst
index c8cc86295d..577b892d57 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,13 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Updated testpmd application.**
+
+ Added support for runt frames in txonly mode. The minimum packet
+ length for ``set txpkts`` is relaxed to the Ethernet header size,
+ since control level protocols can be very short. Checksum offloads
+ are automatically disabled when headers are incomplete.
+
Removed Items
-------------
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index e65376df54..0edcdd8444 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -874,6 +874,17 @@ 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 the Ethernet header
+(``sizeof(struct rte_ether_hdr)``), since control level protocols can
+be very short. This generates runt frames with truncated IPv4/UDP
+headers. Checksum offloads are automatically disabled when the
+corresponding header is not fully present.
+
+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
+lengths.
+
set txtimes
~~~~~~~~~~~
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v4] app/testpmd: support runt frames in txonly
2026-08-07 6:17 [PATCH v4] app/testpmd: support runt frames in txonly Xingui Yang
@ 2026-08-09 19:18 ` Stephen Hemminger
0 siblings, 0 replies; 2+ messages in thread
From: Stephen Hemminger @ 2026-08-09 19:18 UTC (permalink / raw)
To: Xingui Yang
Cc: dev, david.marchand, aman.deep.singh, fengchengwen, lihuisong,
liuyonglong, kangfenglong
On Fri, 7 Aug 2026 14:17:27 +0800
Xingui Yang <yangxingui@huawei.com> wrote:
> Allow setting transmit size to be a small value which has Ethernet
> header but no IP or UDP header, since control level protocols can be
> very short.
>
> Checksum offloads are disabled when headers are incomplete.
> copy_buf_to_pkt_segs stops at the last segment to prevent OOB access.
>
> Suggested-by: Stephen Hemminger <stephen@networkplumber.org>
> Signed-off-by: Xingui Yang <yangxingui@huawei.com>
> ---
> Changes in v4:
> - Removed ultra-small frame support (< 14 bytes) per Stephen's review.
> ---
Still some issues reported by AI.
Also not sure why your email keeps ending up in Spam folder.
Reviewed v4 against main (c1a46b9). Applies cleanly, builds clean with
-Dwerror=true.
Error
-----
app/test-pmd/config.c: set_tx_pkt_segments() is shared with the flowgen
forwarding engine ("set txpkts" is documented as setting the length for
FLOWGEN too), and flowgen still assumes the old 42 byte minimum:
unsigned pkt_size = tx_pkt_length - 4; /* Adjust FCS */
...
pkt->data_len = pkt_size;
ip_hdr->total_length = RTE_CPU_TO_BE_16(pkt_size - sizeof(*eth_hdr));
udp_hdr->dgram_len = RTE_CPU_TO_BE_16(pkt_size - sizeof(*eth_hdr) -
sizeof(*udp_hdr));
With "set txpkts 14", pkt_size is 10. Both length fields underflow in
unsigned arithmetic and are truncated to 16 bits, and flowgen emits
10 byte frames - shorter than the Ethernet header this patch is trying
to guarantee. It also writes 42 bytes of header into an mbuf whose
data_len is 10.
Confirmed:
dpdk-testpmd --vdev='net_pcap0,rx_iface=lo,tx_pcap=/tmp/fg.pcap' -- \
--txpkts=14 --forward-mode=flowgen
every captured frame is 10 bytes: 00010203040100010203
flowgen_begin() should reject tx_pkt_length below
sizeof(rte_ether_hdr) + sizeof(rte_ipv4_hdr) + sizeof(rte_udp_hdr) + 4,
the same way tx_only_begin() guards the split and multi-flow cases.
Warning
-------
doc/guides/testpmd_app_ug/testpmd_funcs.rst: the note listing what
cannot be combined with runt lengths omits "set txtimes".
tx_only_begin() additionally requires sizeof(struct tx_timestamp)
beyond the 42 byte header stack when timestamping is enabled, so runt
lengths fail with -EINVAL there as well.
Info
----
app/test-pmd/txonly.c: the l3_len clamp and the ol_flags masking are
recomputed per packet in the fast path, but the result is invariant.
TX_PKT_SPLIT_RND is already rejected when the first segment is short,
so pkt_len does not vary across packets. Both could be decided once in
tx_only_begin().
app/test-pmd/config.c: the first sentence of the rewritten comment is
still wrong - the loop rejects segments *larger* than
mbuf_data_size[0], not smaller. Worth fixing while touching it.
doc/guides/testpmd_app_ug/testpmd_funcs.rst: spelling out "14 bytes"
reads better than sizeof(struct rte_ether_hdr) in a user guide.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-09 19:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07 6:17 [PATCH v4] app/testpmd: support runt frames in txonly Xingui Yang
2026-08-09 19:18 ` Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox