refactor: introduce Transmit::effective_segment_size

This commit is contained in:
Thomas Eizinger
2025-12-30 07:38:51 +11:00
committed by Dirkjan Ochtman
parent 0dcf9d9db7
commit 99abc2b3e2
2 changed files with 61 additions and 8 deletions
+60
View File
@@ -150,6 +150,25 @@ pub struct Transmit<'a> {
pub src_ip: Option<IpAddr>,
}
impl Transmit<'_> {
/// Computes the effective segment-size of the packet.
///
/// Some (older) network drivers don't like being told to do GSO even if
/// there is effectively only a single segment.
/// (i.e. `segment_size == contents.len()`)
/// Additionally, a `segment_size` that is greater than the content also
/// means there is effectively only a single segment.
/// This case is actually quite common when splitting up a prepared GSO batch
/// again after GSO has been disabled because the last datagram in a GSO
/// batch is allowed to be smaller than the segment size.
fn effective_segment_size(&self) -> Option<usize> {
match self.segment_size? {
size if size >= self.contents.len() => None,
size => Some(size),
}
}
}
/// Log at most 1 IO error per minute
#[cfg(not(wasm_browser))]
const IO_ERROR_LOG_INTERVAL: Duration = std::time::Duration::from_secs(60);
@@ -238,3 +257,44 @@ impl EcnCodepoint {
})
}
}
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use super::*;
#[test]
fn effective_segment_size() {
assert_eq!(
make_transmit(&[0u8; 10], Some(15)).effective_segment_size(),
None,
"segment_size > content_len should yield no effective segment_size"
);
assert_eq!(
make_transmit(&[0u8; 10], Some(10)).effective_segment_size(),
None,
"segment_size == content_len should yield no effective segment_size"
);
assert_eq!(
make_transmit(&[0u8; 10], None).effective_segment_size(),
None,
"no segment_size should yield no effective segment_size"
);
assert_eq!(
make_transmit(&[0u8; 10], Some(5)).effective_segment_size(),
Some(5),
"segment_size < content_len should yield effective segment_size"
);
}
fn make_transmit(contents: &[u8], segment_size: Option<usize>) -> Transmit<'_> {
Transmit {
destination: SocketAddr::from((Ipv4Addr::UNSPECIFIED, 1)),
ecn: None,
contents,
segment_size,
src_ip: None,
}
}
}
+1 -8
View File
@@ -625,14 +625,7 @@ fn prepare_msg(
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
}
// Only set the segment size if it is less than the size of the contents.
// Some network drivers don't like being told to do GSO even if there is effectively only a single segment (i.e. `segment_size == transmit.contents.len()`)
// Additionally, a `segment_size` that is greater than the content also means there is effectively only a single segment.
// This case is actually quite common when splitting up a prepared GSO batch again after GSO has been disabled because the last datagram in a GSO batch is allowed to be smaller than the segment size.
if let Some(segment_size) = transmit
.segment_size
.filter(|segment_size| *segment_size < transmit.contents.len())
{
if let Some(segment_size) = transmit.effective_segment_size() {
gso::set_segment_size(&mut encoder, segment_size as u16);
}