http://bugs.dpdk.org/show_bug.cgi?id=1890
Bug ID: 1890
Summary: r8169: rtl_xmit_pkt corrupts TX descriptor ring on
zero-length mid-chain segment
Product: DPDK
Version: 25.11
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: minor
Priority: Normal
Component: ethdev
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
This is not urgent bug, since giving a driver an mbuf like this is likely
an application bug.
`rtl_xmit_pkt()` in `drivers/net/r8169/r8169_rxtx.c` breaks out of its
segment loop when it encounters a zero-length segment in a multi-segment
mbuf. If earlier segments have already been written to the descriptor ring
(with `FirstFrag` set but no `LastFrag`), the ring is left in a corrupted
state with an incomplete packet owned by the hardware.
In `rtl_xmit_pkt()` (r8169_rxtx.c, line ~1624), the per-segment loop
breaks immediately when it encounters a segment with `data_len == 0`:
```c
for (m_seg = tx_pkt; m_seg; m_seg = m_seg->next) {
opts1 = opts[0];
opts2 = opts[1];
len = m_seg->data_len;
if (len == 0)
break;
txd = &txq->hw_ring[tail];
...
opts1 |= len;
if (m_seg == tx_pkt)
opts1 |= FirstFrag;
if (!m_seg->next)
opts1 |= LastFrag;
...
txd->opts1 = rte_cpu_to_le_32(opts1);
tail = (tail + 1) % nb_tx_desc;
desc_count++;
...
}
txq->tx_tail += desc_count;
txq->tx_free -= desc_count;
```
Consider a 3-segment mbuf where segment 2 (the middle one) has
`data_len == 0`:
1. **Segment 1** (first): written to descriptor ring with `FirstFrag` set.
`DescOwn` is set, so hardware considers it owned.
2. **Segment 2**: `data_len == 0`, loop breaks.
3. **Segment 3** (last): never processed — `LastFrag` is never written.
The result is:
- The descriptor ring contains a partial packet: one or more descriptors
with `DescOwn` and `FirstFrag` but no corresponding `LastFrag`.
- `txq->tx_tail` and `txq->tx_free` are updated to reflect the partial
write (desc_count > 0).
- When the doorbell is rung in `rtl_xmit_pkts()`, the hardware will
attempt to process these descriptors. The behavior is undefined — the
hardware may hang waiting for `LastFrag`, process garbage, or trigger
a DMA error.
- The `tx_clean` path may also malfunction since the sw_ring entries and
descriptor states are inconsistent.
This is a data path corruption bug that could cause the transmit queue to
hang or produce undefined hardware behavior.
## Steps to Reproduce
1. Construct a multi-segment mbuf (nb_segs >= 2) where a non-first
segment has `data_len == 0`.
2. Submit it via `rte_eth_tx_burst()` on an r8169 port.
3. The hardware receives an incomplete descriptor chain.
## Suggested Fix
The zero-length segment check should be handled before any descriptors
are written, or alternatively, zero-length segments should be skipped
with `continue` rather than aborting the entire packet.
**Option A — reject the packet before writing any descriptors** (simplest,
pairs with Bug #1 fix):
Validate all segments up front in `rtl_xmit_pkts()` before calling
`rtl_xmit_pkt()`:
```c
/* Validate all segments before committing to descriptor ring */
bool valid = true;
for (struct rte_mbuf *seg = tx_pkt; seg; seg = seg->next) {
if (seg->data_len == 0) {
valid = false;
break;
}
}
if (!valid) {
rte_pktmbuf_free(tx_pkt);
txq->sw_stats.tx_errors++;
continue;
}
```
**Option B — skip zero-length segments** (if they are considered legal):
```c
if (len == 0)
continue; /* skip empty segments instead of aborting */
```
However, Option B still needs to ensure `LastFrag` is set on the actual
last non-empty segment, which requires restructuring the `LastFrag` logic.
Option A is safer and more straightforward.
--
You are receiving this mail because:
You are the assignee for the bug.