quiche-server: sendmmsg() support when supported.
diff --git a/tools/apps/src/bin/quiche-server.rs b/tools/apps/src/bin/quiche-server.rs
index e7d8828..b04077f 100644
--- a/tools/apps/src/bin/quiche-server.rs
+++ b/tools/apps/src/bin/quiche-server.rs
@@ -86,8 +86,10 @@
let max_datagram_size = MAX_DATAGRAM_SIZE;
let enable_gso = detect_gso(&socket, max_datagram_size);
+ let enable_sendmmsg = detect_sendmmsg();
trace!("GSO detected: {}", enable_gso);
+ trace!("sendmmsg() detected: {}", enable_sendmmsg);
// Create the configuration for the QUIC connections.
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
@@ -522,6 +524,7 @@
&dst_info.unwrap().to,
client.max_datagram_size,
enable_gso,
+ enable_sendmmsg,
) {
if e.kind() == std::io::ErrorKind::WouldBlock {
trace!("send() would block");
diff --git a/tools/apps/src/sendto.rs b/tools/apps/src/sendto.rs
index da7a8ba..059bd09 100644
--- a/tools/apps/src/sendto.rs
+++ b/tools/apps/src/sendto.rs
@@ -92,12 +92,89 @@
panic!("send_to_gso() should not be called on non-linux platforms");
}
+/// Detecting whether sendmmsg() can be used.
+pub fn detect_sendmmsg() -> bool {
+ cfg!(target_os = "linux") ||
+ cfg!(target_os = "android") ||
+ cfg!(target_os = "freebsd") ||
+ cfg!(target_os = "netbsd")
+}
+
+/// Send packets using sendmmsg().
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+fn send_to_sendmmsg(
+ socket: &mio::net::UdpSocket, buf: &[u8], target: &net::SocketAddr,
+ segment_size: usize,
+) -> io::Result<usize> {
+ use nix::sys::socket::sendmmsg;
+ use nix::sys::socket::InetAddr;
+ use nix::sys::socket::MsgFlags;
+ use nix::sys::socket::SendMmsgData;
+ use nix::sys::socket::SockAddr;
+ use nix::sys::uio::IoVec;
+ use std::os::unix::io::AsRawFd;
+
+ let dst = SockAddr::new_inet(InetAddr::from_std(target));
+
+ let mut off = 0;
+ let mut left = buf.len();
+
+ let mut msgs = Vec::new();
+ let mut iovs = Vec::new();
+
+ while left > 0 {
+ let pkt_len = cmp::min(left, segment_size);
+
+ iovs.push([IoVec::from_slice(&buf[off..off + pkt_len])]);
+
+ off += pkt_len;
+ left -= pkt_len;
+ }
+
+ for iov in iovs.iter() {
+ msgs.push(SendMmsgData {
+ iov,
+ cmsgs: &[],
+ addr: Some(dst),
+ _lt: Default::default(),
+ });
+ }
+
+ match sendmmsg(socket.as_raw_fd(), msgs.iter(), MsgFlags::empty()) {
+ Ok(results) => Ok(results.iter().sum()),
+ Err(e) => match e.as_errno() {
+ Some(v) => Err(io::Error::from(v)),
+ None => Err(io::Error::new(io::ErrorKind::Other, e)),
+ },
+ }
+}
+
+/// Send packets using sendmmsg().
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+)))]
+fn send_to_sendmmsg(
+ _socket: &mio::net::UdpSocket, _buf: &[u8], _target: &net::SocketAddr,
+ _segment_size: usize,
+) -> io::Result<usize> {
+ panic!("send_to_sendmmsg() should not be called on non-supported platforms");
+}
+
/// A wrapper function of send_to().
/// - when GSO enabled, send a packet using send_to_gso().
+/// - when sendmmsg() enabled, send a packet using send_to_sendmmsg().
/// Otherwise, send packet using socket.send_to().
pub fn send_to(
socket: &mio::net::UdpSocket, buf: &[u8], target: &net::SocketAddr,
- segment_size: usize, enable_gso: bool,
+ segment_size: usize, enable_gso: bool, enable_sendmmsg: bool,
) -> io::Result<usize> {
if enable_gso {
match send_to_gso(socket, buf, target, segment_size) {
@@ -110,6 +187,17 @@
}
}
+ if enable_sendmmsg {
+ match send_to_sendmmsg(socket, buf, target, segment_size) {
+ Ok(v) => {
+ return Ok(v);
+ },
+ Err(e) => {
+ return Err(e);
+ },
+ }
+ }
+
let mut off = 0;
let mut left = buf.len();
let mut written = 0;