Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase GRO segments to 64 #1354

Merged
merged 1 commit into from May 10, 2022
Merged

Increase GRO segments to 64 #1354

merged 1 commit into from May 10, 2022

Conversation

alessandrod
Copy link
Contributor

Welp, this was a bit of a head scratcher.

While running perf_server and perf_client from two different hosts (and so not on loopback), I noticed that occasionally I was getting the following debug logs:

2022-05-10T02:53:58.233887Z DEBUG drive{id=0}: quinn_proto::connection: failed to authenticate packet

It turns out that these logs are due to truncated GRO segments.

After some kernel spelunking, I noticed this: https://github.com/torvalds/linux/blob/1e8a3f0d2a1ef544611a7ea4a7c1512c732e0e43/net/core/gro.c#L543. The GRO segment size (what we call stride), is set dynamically depending on the first datagram that starts a GRO list. Unless the receive buffer passed to recvmmsg() is an exact multiple of the segment size, segments can be truncated since recvmmsg() will happily fill the whole buffer it's given regardless of whether it's aligned to segment boundaries or not (this is a wart of the API, since in some cases it is ok for the last segment to be shorter than all the others).

The fix is to bump the value we return from gro::gro_segments() to the maximum allowed by the kernel (64, which incidentally is also the value we set for GSO). This way get_max_udp_payload_size() * gro::gro_segments() will always be strictly larger than the GRO lists the kernel produces, so recvmmsg() won't truncate.

@Ralith
Copy link
Collaborator

Ralith commented May 10, 2022

Good catch! Could you include this rationale in the comment so it's not lost?

@alessandrod
Copy link
Contributor Author

Good catch! Could you include this rationale in the comment so it's not lost?

Done!

The GRO segment size (what we call stride), is set dynamically depending
on the first datagram that starts a GRO list [0]. Unless the receive
buffer passed to recvmmsg() is an exact multiple of the segment size,
segments could be truncated since recvmmsg() will happily fill the whole
buffer it's given regardless of whether it's aligned to segment
boundaries or not.

Bump the value returned by gro::gro_segments() to the maximum allowed by
the kernel so that get_max_udp_payload_size() * gro::gro_segments() will
always be large enough to hold the largest GRO list the kernel might
produce, so that recvmmsg() will never truncate.

[0]: https://github.com/torvalds/linux/blob/1e8a3f0d2a1ef544611a7ea4a7c1512c732e0e43/net/core/gro.c#L543
@Ralith Ralith merged commit eab6af5 into quinn-rs:main May 10, 2022
@alessandrod alessandrod deleted the gro branch May 10, 2022 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants