net_tap: include the L4 length in the LSO checksum seed#3812
Open
bitranox wants to merge 1 commit into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a Linux TAP/GSO interoperability issue for Windows/NDIS LSO by rewriting the TCP/UDP checksum seed on segmentation-offload frames so the kernel’s GSO path sees a pseudo-header seed that includes the full L4 length, enabling correct per-segment checksum adjustment during segmentation.
Changes:
- Add
fixup_gso_pseudo_header()to recompute the L4 pseudo-header checksum seed (including full L4 length) for TCP/UDP segmentation-offload frames. - Invoke the fixup for outgoing packets when TCP or UDP segmentation offload is requested.
- Add a unit test validating the rewritten pseudo-header seed value (currently IPv4/TCP).
6a3e7c9 to
b0716e1
Compare
Contributor
Author
|
Thanks. Addressed all three:
|
Contributor
Author
|
@smalis-msft - uh that was bad - You dont do performance tests before merging ? |
A segmentation-offload frame carries a partial TCP/UDP checksum in the checksum field. The netvsp LSO path supplies a length-less pseudo-header there, because the guest cannot know the per-segment length. The kernel GSO path expects the opposite: th->check is the pseudo-header over the full L4 length, as __tcp_v4_send_check writes for a GSO skb, which tcp_gso_segment and the hardware TSO drivers adjust down per segment. Passing the length-less seed straight through leaves the per-segment adjustment short by the segment length, so every emitted segment carries a wrong checksum and an off-host receiver drops it. A guest TCP stream over TSO to an off-host peer collapses to a few Mbit/s of retransmits. Checksum offload without segmentation and same-host traffic are unaffected, since their seed already includes the length. Recompute the seed for segmentation frames from the packet's own IP header. The payload is untouched; the kernel or NIC still completes the per-segment checksum.
b0716e1 to
5044998
Compare
Contributor
Author
|
@smalis-msft - I guess make that sooner then later - NIC TX Performance was down from around 10GBit to 30MBit because of checksum errors due to #3730 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A guest TCP stream sent with TSO/LSO to an off-host receiver collapses to
a few Mbit/s with heavy retransmits. The same guest is fine with
segmentation offload disabled, to a same-host peer, and on receive.
Cause
For a segmentation-offload (GSO) frame, net_tap forwards the frontend's
partial TCP/UDP checksum to the kernel unchanged. The netvsp LSO path
supplies a length-less pseudo-header seed (the guest cannot know the
per-segment length). The kernel GSO path expects th->check to be the
pseudo-header over the full L4 length, as __tcp_v4_send_check writes for a
GSO skb; tcp_gso_segment and the hardware TSO drivers then adjust it down
per segment. With a length-less seed the adjustment is short by the
segment length, so every segment goes out with a wrong checksum and the
receiver drops it. Checksum offload without segmentation is unaffected,
because the non-segmented seed already includes the length.
Fix
For segmentation frames, recompute the L4 pseudo-header seed in net_tap
from the packet's own IP header, including the full L4 length. The payload
is left untouched; the kernel or NIC still completes the per-segment
checksum.
Testing
Unit test for the seed value. On a Linux guest over the TAP backend, an
off-host TCP TX recovered from a few Mbit/s to ~9 Gbit/s, and a checksum
capture at the egress NIC showed zero bad checksums across ~1.8M segments
(previously ~16% bad).