Skip to content

Commit

Permalink
GSO platform support
Browse files Browse the repository at this point in the history
This splits out the platform/socket parts for UDP GSO support
from quinn-rs#953 to reduce the amount of code to review.

This change mainly implements setting the segment size
socket option, and adds a runtime detection mechanism for
GSO support.
  • Loading branch information
Matthias247 committed Jan 3, 2021
1 parent 79361e2 commit e05477d
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 2 deletions.
2 changes: 2 additions & 0 deletions quinn-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ where
destination,
contents: buf,
ecn: None,
segment_size: None,
});
}
}
Expand Down Expand Up @@ -559,6 +560,7 @@ where
} else {
None
},
segment_size: None,
})
}

Expand Down
4 changes: 4 additions & 0 deletions quinn-proto/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ where
destination: remote,
ecn: None,
contents: buf,
segment_size: None,
});
return None;
}
Expand Down Expand Up @@ -334,6 +335,7 @@ where
destination: remote,
ecn: None,
contents: buf,
segment_size: None,
});
}

Expand Down Expand Up @@ -587,6 +589,7 @@ where
destination: remote,
ecn: None,
contents: buf,
segment_size: None,
});
return None;
}
Expand Down Expand Up @@ -679,6 +682,7 @@ where
destination,
ecn: None,
contents: buf,
segment_size: None,
})
}

Expand Down
3 changes: 3 additions & 0 deletions quinn-proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ pub struct Transmit {
pub ecn: Option<EcnCodepoint>,
/// Contents of the datagram
pub contents: Vec<u8>,
/// The segment size if this transmission contains multiple datagrams.
/// This is `None` if the transmit only contains a single datagram
pub segment_size: Option<usize>,
}

//
Expand Down
3 changes: 3 additions & 0 deletions quinn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ tracing = "0.1.10"
tokio = { version = "0.2.6", features = ["rt-core", "io-driver", "time"] }
webpki = { version = "0.21", optional = true }

[target.'cfg(unix)'.dependencies]
lazy_static = "1"

[dev-dependencies]
anyhow = "1.0.22"
crc = "1.8.1"
Expand Down
5 changes: 5 additions & 0 deletions quinn/src/platform/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ impl super::UdpExt for UdpSocket {
}
}

/// Returns the platforms UDP socket capabilities
pub fn caps() -> super::UdpCapabilities {
super::UdpCapabilities { gso: false }
}

pub const BATCH_SIZE: usize = 1;
13 changes: 13 additions & 0 deletions quinn/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ mod imp;
#[path = "fallback.rs"]
mod imp;

#[allow(dead_code)] // TODO: Remove when used
/// Returns the platforms UDP socket capabilities
pub fn caps() -> UdpCapabilities {
imp::caps()
}

/// Number of UDP packets to send/receive at a time
pub const BATCH_SIZE: usize = imp::BATCH_SIZE;

Expand All @@ -23,3 +29,10 @@ pub trait UdpExt {
fn send_ext(&self, transmits: &[Transmit]) -> io::Result<usize>;
fn recv_ext(&self, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize>;
}

/// The capabilities a UDP socket suppports on a certain platform
#[derive(Debug, Clone, Copy)]
pub struct UdpCapabilities {
/// Whether the platform supports Generic Send Offload (GSO)
pub gso: bool,
}
67 changes: 65 additions & 2 deletions quinn/src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ use std::{
ptr,
};

use lazy_static::lazy_static;
use mio::net::UdpSocket;
use proto::{EcnCodepoint, Transmit};

use super::cmsg;
use super::{cmsg, UdpCapabilities, UdpExt};
use crate::udp::RecvMeta;

#[cfg(target_os = "freebsd")]
type IpTosTy = libc::c_uchar;
#[cfg(not(target_os = "freebsd"))]
type IpTosTy = libc::c_int;

impl super::UdpExt for UdpSocket {
impl UdpExt for UdpSocket {
fn init_ext(&self) -> io::Result<()> {
// Safety
assert_eq!(
Expand Down Expand Up @@ -267,6 +268,11 @@ impl super::UdpExt for UdpSocket {
}
}

/// Returns the platforms UDP socket capabilities
pub fn caps() -> UdpCapabilities {
*CAPABILITIES
}

const CMSG_LEN: usize = 64;

fn prepare_msg(
Expand Down Expand Up @@ -296,6 +302,26 @@ fn prepare_msg(
} else {
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
}

if let Some(segment_size) = transmit.segment_size {
debug_assert!(
caps().gso,
"Platform must support GSO for setting segment size"
);

#[cfg(target_os = "linux")]
fn set_segment_size(encoder: &mut cmsg::Encoder, segment_size: u16) {
encoder.push(libc::SOL_UDP, libc::UDP_SEGMENT, segment_size);
}

#[cfg(not(target_os = "linux"))]
fn set_segment_size(_encoder: &mut cmsg::Encoder, _segment_size: u16) {
panic!("Setting a segment size is not supported on current platform");
}

set_segment_size(&mut encoder, segment_size as u16);
}

encoder.finish();
}

Expand Down Expand Up @@ -373,3 +399,40 @@ pub const BATCH_SIZE: usize = 32;

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub const BATCH_SIZE: usize = 1;

#[cfg(target_os = "linux")]
lazy_static! {
static ref CAPABILITIES: UdpCapabilities = {
/// Checks whether GSO support is available by setting the UDP_SEGMENT
/// option on a socket
fn supports_gso() -> bool {
const GSO_SIZE: libc::c_int = 1500;

let socket = match std::net::UdpSocket::bind("[::]:0") {
Ok(socket) => socket,
Err(_) => return false,
};

let rc = unsafe {
libc::setsockopt(
socket.as_raw_fd(),
libc::SOL_UDP,
libc::UDP_SEGMENT,
&GSO_SIZE as *const _ as _,
mem::size_of_val(&GSO_SIZE) as _,
)
};

rc != -1
}

UdpCapabilities {
gso: supports_gso(),
}
};
}

#[cfg(not(target_os = "linux"))]
lazy_static! {
pub static ref CAPABILITIES: UdpCapabilities = UdpCapabilities { gso: false };
}

0 comments on commit e05477d

Please sign in to comment.