s2n_quic_platform/syscall/
mmsg.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use super::{SocketEvents, SocketType, UnixMessage};
5use crate::socket::stats;
6use libc::mmsghdr;
7use std::os::unix::io::{AsRawFd, RawFd};
8
9impl UnixMessage for mmsghdr {
10    #[inline]
11    fn send<E: SocketEvents>(
12        fd: RawFd,
13        entries: &mut [Self],
14        events: &mut E,
15        stats: &stats::Sender,
16    ) {
17        send(&fd, entries, events, stats)
18    }
19
20    #[inline]
21    fn recv<E: SocketEvents>(
22        fd: RawFd,
23        ty: SocketType,
24        entries: &mut [Self],
25        events: &mut E,
26        stats: &stats::Sender,
27    ) {
28        recv(&fd, ty, entries, events, stats)
29    }
30}
31
32#[inline]
33pub fn send<Sock: AsRawFd, E: SocketEvents>(
34    socket: &Sock,
35    packets: &mut [mmsghdr],
36    events: &mut E,
37    stats: &stats::Sender,
38) {
39    if packets.is_empty() {
40        return;
41    }
42
43    // Safety: calling a libc function is inherently unsafe as rust cannot
44    // make any invariant guarantees. This has to be reviewed by humans instead
45    // so the [docs](https://linux.die.net/man/2/sendmmsg) are inlined here:
46
47    // > The sockfd argument is the file descriptor of the socket on which data
48    // > is to be transmitted.
49    let sockfd = socket.as_raw_fd();
50
51    // > The msgvec argument is a pointer to an array of mmsghdr structures.
52    //
53    // > The msg_hdr field is a msghdr structure, as described in sendmsg(2).
54    // > The msg_len field is used to return the number of bytes sent from the
55    // > message in msg_hdr.
56    let msgvec = packets.as_mut_ptr();
57
58    // > The size of this array is specified in vlen.
59    //
60    // > The value specified in vlen is capped to UIO_MAXIOV (1024).
61    let vlen = packets.len().min(1024) as _;
62
63    // > The flags argument contains flags ORed together.
64    //
65    // No flags are currently set
66    let flags = Default::default();
67
68    // > The sendmmsg() system call is an extension of sendmsg(2) that allows
69    // > the caller to transmit multiple messages on a socket using a single
70    // > system call. (This has performance benefits for some applications.)
71    //
72    // > A nonblocking call sends as many messages as possible (up to the limit
73    // > specified by vlen) and returns immediately.
74    //
75    // > On return from sendmmsg(), the msg_len fields of successive elements
76    // > of msgvec are updated to contain the number of bytes transmitted from
77    // > the corresponding msg_hdr. The return value of the call indicates the
78    // > number of elements of msgvec that have been updated.
79    //
80    // > On success, sendmmsg() returns the number of messages sent from msgvec;
81    // > if this is less than vlen, the caller can retry with a further sendmmsg()
82    // > call to send the remaining messages.
83    //
84    // > On error, -1 is returned, and errno is set to indicate the error.
85
86    let res = libc!(sendmmsg(sockfd, msgvec, vlen, flags));
87
88    stats.send().on_operation_result(&res, |count| *count as _);
89
90    let _ = match res {
91        Ok(count) => events.on_complete(count as _),
92        Err(error) => events.on_error(error),
93    };
94}
95
96#[inline]
97pub fn recv<Sock: AsRawFd, E: SocketEvents>(
98    socket: &Sock,
99    socket_type: SocketType,
100    packets: &mut [mmsghdr],
101    events: &mut E,
102    stats: &stats::Sender,
103) {
104    if packets.is_empty() {
105        return;
106    }
107
108    // Safety: calling a libc function is inherently unsafe as rust cannot
109    // make any invariant guarantees. This has to be reviewed by humans instead
110    // so the [docs](https://linux.die.net/man/2/recvmmsg) are inlined here:
111
112    // > The sockfd argument is the file descriptor of the socket to receive data from.
113    let sockfd = socket.as_raw_fd();
114
115    // > The msgvec argument is a pointer to an array of mmsghdr structures.
116    //
117    // > The msg_len field is the number of bytes returned for the message in the entry.
118    let msgvec = packets.as_mut_ptr();
119
120    // > The size of this array is specified in vlen.
121    let vlen = packets.len() as _;
122
123    // > The flags argument contains flags ORed together.
124    //
125    // If the socket is blocking, set the MSG_WAITFORONE flag so we don't hang until the entire
126    // buffer is full.
127    let flags = match socket_type {
128        SocketType::Blocking => libc::MSG_WAITFORONE,
129        SocketType::NonBlocking => libc::MSG_DONTWAIT,
130    };
131
132    // some platforms have a mismatch in types for the flag values and the actual parameter so cast
133    // it to whatever type the syscall expects.
134    let flags = flags as _;
135
136    // > The timeout argument points to a struct timespec defining a timeout
137    // > (seconds plus nanoseconds) for the receive operation.
138    //
139    // Since we currently only use non-blocking sockets, this isn't needed.
140    // If support is added for non-blocking sockets, this will need to be
141    // updated.
142    let timeout = core::ptr::null_mut();
143
144    // > The recvmmsg() system call is an extension of recvmsg(2)
145    // > that allows the caller to receive multiple messages from a
146    // > socket using a single system call.
147    //
148    // > A nonblocking call reads as many messages as are available
149    // > (up to the limit specified by vlen) and returns immediately.
150    //
151    // > On return from recvmmsg(), successive elements of msgvec are
152    // > updated to contain information about each received message:
153    // > msg_len contains the size of the received message;
154    // > the subfields of msg_hdr are updated as described in recvmsg(2).
155    // > The return value of the call indicates the number of elements of
156    // > msgvec that have been updated.
157    //
158    // > On success, recvmmsg() returns the number of messages received in
159    // > msgvec; on error, -1 is returned, and errno is set to indicate the error.
160
161    let res = libc!(recvmmsg(sockfd, msgvec, vlen, flags, timeout));
162
163    stats.recv().on_operation_result(&res, |count| *count as _);
164
165    let _ = match res {
166        Ok(count) => events.on_complete(count as _),
167        Err(error) => events.on_error(error),
168    };
169}