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}