git: 1d445ae68446 - main - dpaa/eth: fast-path single-page TX frame build
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 22 Aug 2026 16:04:54 UTC
The branch main has been updated by jhibbits:
URL: https://cgit.FreeBSD.org/src/commit/?id=1d445ae68446771c78161a3eca35acdb04ef78ab
commit 1d445ae68446771c78161a3eca35acdb04ef78ab
Author: Justin Hibbits <jhibbits@FreeBSD.org>
AuthorDate: 2026-08-10 18:24:08 +0000
Commit: Justin Hibbits <jhibbits@FreeBSD.org>
CommitDate: 2026-08-22 16:03:31 +0000
dpaa/eth: fast-path single-page TX frame build
The TX SG-build loop in dpaa_eth_if_start_locked() walked page
boundaries with PAGE_MASK arithmetic even for buffers that lived
entirely within one page -- the common case, since MCLBYTES
is smaller than PAGE_SIZE. Add a fast path that emits a single SGT
entry for wholly-in-one-page segments and skips the inner while
entirely.
Fix the following bugs while we're here:
1. "if (m->m_len == 0) continue;" in the outer loop never
advanced m -- any zero-length mbuf hung the TX path in an
infinite loop. Fix this by switching to a for loop, with the
advancement in the post-clause.
2. In the inner (page-splitting) loop, the cap
"if (m->m_len < ssize) ssize = m->m_len;" compared against
the mbuf's original length, not the remaining bytes. A single mbuf
whose data started mid-page and ran into a second page would produce
a second SGT entry with ssize > rem, over-reading past the buffer end
into whatever followed in kernel memory. Fixed by tracking a local
rem and capping ssize against it.
3. If the whole mbuf chain consisted of zero-length segments,
the final-flag store "fi_sgt[i - 1].final = 1" wrote to
index -1. Reject empty frames up front now instead.
As part of this, rename the inner counter from dsize to rem for clarity
instead of playing double-duty in both inner and outer loops.
---
sys/dev/dpaa/dpaa_eth.c | 57 ++++++++++++++++++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 15 deletions(-)
diff --git a/sys/dev/dpaa/dpaa_eth.c b/sys/dev/dpaa/dpaa_eth.c
index 2fb902c4d300..e2826eb70b14 100644
--- a/sys/dev/dpaa/dpaa_eth.c
+++ b/sys/dev/dpaa/dpaa_eth.c
@@ -670,7 +670,7 @@ dpaa_eth_tx_add_csum(struct dpaa_eth_frame_info *fi)
void
dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
{
- vm_size_t dsize, psize, ssize;
+ vm_size_t psize, ssize;
struct dpaa_eth_frame_info *fi;
unsigned int i;
struct mbuf *m0, *m;
@@ -710,44 +710,71 @@ dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
}
i = 0;
- m = m0;
psize = 0;
- dsize = 0;
fi->fi_mbuf = m0;
- while (m && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
+ for (m = m0; m != NULL && i < DPAA_NUM_OF_SG_TABLE_ENTRY;
+ m = m->m_next) {
+ vm_size_t rem;
+
if (m->m_len == 0)
continue;
- dsize = m->m_len;
vaddr = (vm_offset_t)m->m_data;
- while (dsize > 0 && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
+ rem = m->m_len;
+
+ /*
+ * Fast path: the whole segment lives inside one
+ * page. Covers every default-cluster mbuf
+ * (MCLBYTES < PAGE_SIZE) and skips the split loop
+ * in the common case.
+ */
+ if ((vaddr & PAGE_MASK) + rem <= PAGE_SIZE) {
+ fi->fi_sgt[i].addr = dpaa_eth_va_to_phys(vaddr);
+ fi->fi_sgt[i].length = rem;
+ fi->fi_sgt[i].extension = 0;
+ fi->fi_sgt[i].final = 0;
+ fi->fi_sgt[i].bpid = 0;
+ fi->fi_sgt[i].offset = 0;
+ psize += rem;
+ i++;
+ continue;
+ }
+
+ /*
+ * Slow path: mbuf crosses at least one page
+ * boundary (jumbo cluster, or an unusually-offset
+ * external buffer). Emit one SGT entry per
+ * contiguous physical span.
+ */
+ while (rem > 0 && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
ssize = PAGE_SIZE - (vaddr & PAGE_MASK);
- if (m->m_len < ssize)
- ssize = m->m_len;
+ if (rem < ssize)
+ ssize = rem;
fi->fi_sgt[i].addr = dpaa_eth_va_to_phys(vaddr);
fi->fi_sgt[i].length = ssize;
-
fi->fi_sgt[i].extension = 0;
fi->fi_sgt[i].final = 0;
fi->fi_sgt[i].bpid = 0;
fi->fi_sgt[i].offset = 0;
- dsize -= ssize;
+ rem -= ssize;
vaddr += ssize;
psize += ssize;
i++;
}
- if (dsize > 0)
+ if (rem > 0) /* SGT full mid-mbuf */
break;
-
- m = m->m_next;
}
- /* Check if SG table was constructed properly */
- if (m != NULL || dsize != 0) {
+ /*
+ * Reject the frame if we didn't consume the whole chain
+ * (SGT full mid-frame) or if the chain produced no SGT
+ * entries at all (all-zero-length mbufs).
+ */
+ if (m != NULL || i == 0) {
dpaa_eth_fi_free(sc, fi);
m_freem(m0);
continue;