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 Dec 31, 2020
1 parent 62a8800 commit 15a34be
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 3 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 @@ -388,6 +388,7 @@ where
destination,
contents: buf,
ecn: None,
segment_size: None,
});
}
}
Expand Down Expand Up @@ -568,6 +569,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 @@ -191,6 +191,7 @@ where
destination: remote,
ecn: None,
contents: buf,
segment_size: None,
});
return None;
}
Expand Down Expand Up @@ -336,6 +337,7 @@ where
destination: remote,
ecn: None,
contents: buf,
segment_size: None,
});
}

Expand Down Expand Up @@ -584,6 +586,7 @@ where
destination: remote,
ecn: None,
contents: buf,
segment_size: None,
});
return None;
}
Expand Down Expand Up @@ -677,6 +680,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
4 changes: 4 additions & 0 deletions quinn/src/platform/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ impl super::UdpExt for UdpSocket {
};
Ok(1)
}

fn caps() -> super::UdpCapabilities {
super::UdpCapabilities { gso: false }
}
}

pub const BATCH_SIZE: usize = 1;
8 changes: 8 additions & 0 deletions quinn/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ pub trait UdpExt {
fn init_ext(&self) -> io::Result<()>;
fn send_ext(&self, transmits: &[Transmit]) -> io::Result<usize>;
fn recv_ext(&self, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize>;
fn caps() -> UdpCapabilities;
}

/// 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,
}
70 changes: 67 additions & 3 deletions quinn/src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{
ptr,
};

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

Expand All @@ -20,6 +21,8 @@ type IpTosTy = libc::c_int;

impl super::UdpExt for UdpSocket {
fn init_ext(&self) -> io::Result<()> {
Self::caps();

// Safety
assert_eq!(
mem::size_of::<SocketAddrV4>(),
Expand Down Expand Up @@ -148,7 +151,7 @@ impl super::UdpExt for UdpSocket {
let mut iovecs: [libc::iovec; BATCH_SIZE] = unsafe { mem::zeroed() };
let mut cmsgs = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
for (i, transmit) in transmits.iter().enumerate().take(BATCH_SIZE) {
prepare_msg(
prepare_msg::<Self>(
transmit,
&mut msgs[i].msg_hdr,
&mut iovecs[i],
Expand Down Expand Up @@ -182,7 +185,7 @@ impl super::UdpExt for UdpSocket {
let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
let mut sent = 0;
while sent < transmits.len() {
prepare_msg(&transmits[sent], &mut hdr, &mut iov, &mut ctrl);
prepare_msg::<Self>(&transmits[sent], &mut hdr, &mut iov, &mut ctrl);
let n = unsafe { libc::sendmsg(self.as_raw_fd(), &hdr, 0) };
if n == -1 {
let e = io::Error::last_os_error();
Expand Down Expand Up @@ -265,11 +268,15 @@ impl super::UdpExt for UdpSocket {
meta[0] = decode_recv(&name, &hdr, n as usize);
Ok(1)
}

fn caps() -> super::UdpCapabilities {
*CAPABILITIES
}
}

const CMSG_LEN: usize = 64;

fn prepare_msg(
fn prepare_msg<U: super::UdpExt>(
transmit: &Transmit,
hdr: &mut libc::msghdr,
iov: &mut libc::iovec,
Expand All @@ -296,6 +303,26 @@ fn prepare_msg(
} else {
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
}

if let Some(segment_size) = transmit.segment_size {
debug_assert!(
U::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 +400,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! {
pub static ref CAPABILITIES: super::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
}

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

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

0 comments on commit 15a34be

Please sign in to comment.