<div dir="auto"><div dir="auto"><div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Le jeu. 11 juin 2026, 19:31, Stephen Hemminger <<a href="mailto:stephen@networkplumber.org" target="_blank" rel="noreferrer">stephen@networkplumber.org</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Thu, 11 Jun 2026 17:49:24 +0200<br>
Maxime Leroy <<a href="mailto:maxime@leroys.fr" rel="noreferrer noreferrer" target="_blank">maxime@leroys.fr</a>> wrote:<br>
<br>
> It saves a forwarding application nothing: the datapath reads the L2<br>
> header anyway to classify or strip. The offload does not remove that<br>
> read, it relocates it into the driver Rx burst, where it is far more<br>
> expensive.<br>
> <br>
> The cost is a matter of timing. rte_vlan_strip() reaches the L2 header<br>
> through rte_pktmbuf_mtod(), which dereferences mbuf->buf_addr. On a<br>
> freshly recycled buffer that mbuf cacheline is cold. eth_fd_to_mbuf()<br>
> has just written other fields of it (data_off, ol_flags), but buf_addr<br>
> is a persistent field it does not rewrite. A write does not stall: it<br>
> posts to the store buffer while the line fills in the background, and<br>
> the rewritten fields are forwarded straight from there. buf_addr has<br>
> nothing to forward, so it must be read from the line, whose fill is<br>
> still in flight, and the read stalls. The ethertype read that follows,<br>
> on the cold payload line, stalls again. Read later by the application,<br>
> when the fill has completed, the same read hits. The offload just<br>
> performs it at the worst possible moment.<br>
> <br>
> Measured on a single-core port-to-port forwarding test over two 10G<br>
> ports (one core at 2 GHz, 64-byte untagged frames):<br>
> <br>
>   - throughput 4.22 -> 5.00 Mpps (+18 percent)<br>
>   - IPC 0.93 -> 1.25: the cost was memory stall, not compute<br>
>   - L3/DRAM-bound L2 refills 319M -> 200M over 10s (-37 percent)<br>
> <br>
> perf confirms it: with the offload, the buf_addr load (the cold mbuf<br>
> field) and the payload load account for about 84 percent of the Rx<br>
> burst's L2 refills; removing it, those vanish and only the inherent DQRR<br>
> dequeue misses remain.<br>
> <br>
> Stop advertising VLAN_STRIP and remove the rte_vlan_strip() calls from<br>
> every Rx path. This is a behavioural change: the tag is left in the<br>
> frame, so an application must strip it itself, on the L2 header it<br>
> already reads.<br>
> <br>
> Signed-off-by: Maxime Leroy <<a href="mailto:maxime@leroys.fr" rel="noreferrer noreferrer" target="_blank">maxime@leroys.fr</a>><br>
> ---<br>
<br>
In general I agree, but you overstate the impact. Any real application<br>
is going to look at the mbuf anyway. Relying on testpmd numbers is BS.<br>
<br>
The NBL driver does the same thing.<br>
So does PCAP but it has no choice, and is slow anyway.<br>
Virtio/vhost does as well.</blockquote></div></div></div><div dir="auto"><br></div><div dir="auto">This was not measured with testpmd, but with Grout in I/O forwarding mode.</div><div dir="auto"><br></div><div dir="auto">The comparison is exactly between Grout's software fallback and the advertised offload path. Without VLAN_STRIP, Grout's rx_process() reads the Ethernet header and strips the VLAN tag itself if needed. With VLAN_STRIP enabled, Grout uses rx_offload_process(), which only consumes</div><div dir="auto">RTE_MBUF_F_RX_VLAN_STRIPPED/vlan_tci and does not inspect the Ethernet header</div><div dir="auto">for VLAN stripping.</div><div dir="auto"><br></div><div dir="auto">For dpaa2, however, VLAN_STRIP is not done by the device. The PMD implements the advertised offload by calling rte_vlan_strip() in the Rx burst path. So enabling the "offload" just moves the same software work from Grout into the driver.</div><div dir="auto"><br></div><div dir="auto">The cost is timing. rte_vlan_strip() calls rte_pktmbuf_mtod(), which needs mbuf->buf_addr. That value is persistent mbuf metadata; it is not produced by the FD-to-mbuf conversion. eth_fd_to_mbuf() has just written other mbuf fields such as data_off and ol_flags; those writes can be posted or forwarded, but</div><div dir="auto">they do not provide buf_addr. If the mbuf cacheline is cold, the buf_addr load</div><div dir="auto">has to wait for that line to be fetched before the driver can reach the Ethernet header.</div><div dir="auto"><br></div><div dir="auto">Grout does the same L2 read later in rx_process(), where it is already processing L2. So the fake PMD offload performs the same software fallback, but injects an extra mbuf-metadata dependency at a worse point in the Rx burst path.</div><div dir="auto"><div dir="auto"><br></div></div></div>