Skip to content

Commit e9350d0

Browse files
committed
refactor(quinn-udp): split fast&slow send/recv paths
Separate the fast path (`msghdr_x`-based) and slow path (`msghdr`-based) implementations to prepare for runtime dispatch between them. Broken out of #2463 as suggested by @djc.
1 parent 4742a70 commit e9350d0

File tree

2 files changed

+95
-6
lines changed

2 files changed

+95
-6
lines changed

quinn-udp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ impl Transmit<'_> {
161161
/// This case is actually quite common when splitting up a prepared GSO batch
162162
/// again after GSO has been disabled because the last datagram in a GSO
163163
/// batch is allowed to be smaller than the segment size.
164+
#[cfg_attr(apple_fast, allow(dead_code))] // Used by prepare_msg, which is unused when apple_fast
164165
fn effective_segment_size(&self) -> Option<usize> {
165166
match self.segment_size? {
166167
size if size >= self.contents.len() => None,

quinn-udp/src/unix.rs

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io:
397397
.enumerate()
398398
.take(BATCH_SIZE)
399399
{
400-
prepare_msg(
400+
prepare_msg_x(
401401
&Transmit {
402402
destination: transmit.destination,
403403
ecn: transmit.ecn,
@@ -433,6 +433,12 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io:
433433

434434
#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
435435
fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
436+
send_single(state, io, transmit)
437+
}
438+
439+
#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple))]
440+
#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
441+
fn send_single(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
436442
let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
437443
let mut iov: libc::iovec = unsafe { mem::zeroed() };
438444
let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
@@ -523,7 +529,7 @@ fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) ->
523529
let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
524530
let max_msg_count = bufs.len().min(BATCH_SIZE);
525531
for i in 0..max_msg_count {
526-
prepare_recv(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
532+
prepare_recv_x(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
527533
}
528534
let msg_count = loop {
529535
let n = unsafe { recvmsg_x(io.as_raw_fd(), hdrs.as_mut_ptr(), max_msg_count as _, 0) };
@@ -553,6 +559,22 @@ fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) ->
553559
apple_slow
554560
))]
555561
fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
562+
recv_single(io, bufs, meta)
563+
}
564+
565+
#[cfg(any(
566+
target_os = "openbsd",
567+
target_os = "netbsd",
568+
target_os = "dragonfly",
569+
solarish,
570+
apple
571+
))]
572+
#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
573+
fn recv_single(
574+
io: SockRef<'_>,
575+
bufs: &mut [IoSliceMut<'_>],
576+
meta: &mut [RecvMeta],
577+
) -> io::Result<usize> {
556578
let mut name = MaybeUninit::<libc::sockaddr_storage>::uninit();
557579
let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit());
558580
let mut hdr = unsafe { mem::zeroed::<libc::msghdr>() };
@@ -581,11 +603,11 @@ fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) ->
581603

582604
const CMSG_LEN: usize = 88;
583605

606+
#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
584607
fn prepare_msg(
585608
transmit: &Transmit<'_>,
586609
dst_addr: &socket2::SockAddr,
587-
#[cfg(not(apple_fast))] hdr: &mut libc::msghdr,
588-
#[cfg(apple_fast)] hdr: &mut msghdr_x,
610+
hdr: &mut libc::msghdr,
589611
iov: &mut libc::iovec,
590612
ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
591613
#[allow(unused_variables)] // only used on FreeBSD & macOS
@@ -625,6 +647,10 @@ fn prepare_msg(
625647
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
626648
}
627649

650+
// On apple_fast, prepare_msg is only compiled for send_single (fallback path), while the main
651+
// send path uses prepare_msg_x with msghdr_x. gso::set_segment_size has a different signature
652+
// when apple_fast is enabled, and it's a no-op on non-Linux platforms anyway.
653+
#[cfg(not(apple_fast))]
628654
if let Some(segment_size) = transmit.effective_segment_size() {
629655
gso::set_segment_size(&mut encoder, segment_size as u16);
630656
}
@@ -668,7 +694,67 @@ fn prepare_msg(
668694
encoder.finish();
669695
}
670696

671-
#[cfg(not(apple_fast))]
697+
/// Prepares an `msghdr_x` for use with `sendmsg_x`.
698+
#[cfg(apple_fast)]
699+
fn prepare_msg_x(
700+
transmit: &Transmit<'_>,
701+
dst_addr: &socket2::SockAddr,
702+
hdr: &mut msghdr_x,
703+
iov: &mut libc::iovec,
704+
ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
705+
#[allow(unused_variables)] encode_src_ip: bool,
706+
sendmsg_einval: bool,
707+
) {
708+
iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
709+
iov.iov_len = transmit.contents.len();
710+
711+
let name = dst_addr.as_ptr() as *mut libc::c_void;
712+
let namelen = dst_addr.len();
713+
hdr.msg_name = name as *mut _;
714+
hdr.msg_namelen = namelen;
715+
hdr.msg_iov = iov;
716+
hdr.msg_iovlen = 1;
717+
718+
hdr.msg_control = ctrl.0.as_mut_ptr() as _;
719+
hdr.msg_controllen = CMSG_LEN as _;
720+
let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
721+
let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
722+
let is_ipv4 = transmit.destination.is_ipv4()
723+
|| matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
724+
if is_ipv4 {
725+
if !sendmsg_einval {
726+
encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
727+
}
728+
} else {
729+
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
730+
}
731+
732+
if let Some(ip) = &transmit.src_ip {
733+
match ip {
734+
IpAddr::V4(v4) => {
735+
if encode_src_ip {
736+
let addr = libc::in_addr {
737+
s_addr: u32::from_ne_bytes(v4.octets()),
738+
};
739+
encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
740+
}
741+
}
742+
IpAddr::V6(v6) => {
743+
let pktinfo = libc::in6_pktinfo {
744+
ipi6_ifindex: 0,
745+
ipi6_addr: libc::in6_addr {
746+
s6_addr: v6.octets(),
747+
},
748+
};
749+
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
750+
}
751+
}
752+
}
753+
754+
encoder.finish();
755+
}
756+
757+
#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
672758
fn prepare_recv(
673759
buf: &mut IoSliceMut<'_>,
674760
name: &mut MaybeUninit<libc::sockaddr_storage>,
@@ -684,8 +770,9 @@ fn prepare_recv(
684770
hdr.msg_flags = 0;
685771
}
686772

773+
/// Prepares an `msghdr_x` for receiving with `recvmsg_x`.
687774
#[cfg(apple_fast)]
688-
fn prepare_recv(
775+
fn prepare_recv_x(
689776
buf: &mut IoSliceMut<'_>,
690777
name: &mut MaybeUninit<libc::sockaddr_storage>,
691778
ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
@@ -1006,6 +1093,7 @@ mod gso {
10061093
}
10071094
}
10081095

1096+
#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
10091097
pub(super) fn set_segment_size(
10101098
#[cfg(not(apple_fast))] _encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
10111099
#[cfg(apple_fast)] _encoder: &mut cmsg::Encoder<'_, msghdr_x>,

0 commit comments

Comments
 (0)