1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-24 04:01:07 +03:00
Files
mvfst/quic/common/udpsocket/QuicAsyncUDPSocketImpl.cpp
Matt Joras aefc9e369b Introduce quic::Optional
Summary:
The idea here is to make it so we can swap out the type we are using for optionality. In the near term we are going to try swapping towards one that more aggressively tries to save size.

For now there is no functional change and this is just a big aliasing diff.

Reviewed By: sharmafb

Differential Revision: D57633896

fbshipit-source-id: 6eae5953d47395b390016e59cf9d639f3b6c8cfe
2024-06-11 11:02:02 -07:00

198 lines
6.3 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <quic/common/udpsocket/QuicAsyncUDPSocketImpl.h>
namespace {
constexpr socklen_t kAddrLen = sizeof(sockaddr_storage);
} // namespace
namespace quic {
QuicAsyncUDPSocket::RecvResult QuicAsyncUDPSocketImpl::recvmmsgNetworkData(
uint64_t readBufferSize,
uint16_t numPackets,
NetworkData& networkData,
Optional<folly::SocketAddress>& peerAddress,
size_t& totalData) {
/**
* This is largely a copy / paste of QuicClientTransport::recvmmsg.
*
* TODO(bschlinker): Refactor and add dedicated testing.
*/
recvmmsgStorage_.resize(numPackets);
auto& msgs = recvmmsgStorage_.msgs;
int flags = 0;
#ifdef FOLLY_HAVE_MSG_ERRQUEUE
bool useGRO = getGRO() > 0;
bool checkCmsgs = useGRO || getTimestamping() > 0 || getRecvTos();
std::vector<std::array<
char,
QuicAsyncUDPSocket::ReadCallback::OnDataAvailableParams::kCmsgSpace>>
controlVec(checkCmsgs ? numPackets : 0);
// we need to consider MSG_TRUNC too
if (useGRO) {
flags |= MSG_TRUNC;
}
#endif
for (uint16_t i = 0; i < numPackets; ++i) {
auto& addr = recvmmsgStorage_.impl_[i].addr;
auto& readBuffer = recvmmsgStorage_.impl_[i].readBuffer;
auto& iovec = recvmmsgStorage_.impl_[i].iovec;
struct msghdr* msg = &msgs[i].msg_hdr;
if (!readBuffer) {
readBuffer = folly::IOBuf::createCombined(readBufferSize);
iovec.iov_base = readBuffer->writableData();
iovec.iov_len = readBufferSize;
msg->msg_iov = &iovec;
msg->msg_iovlen = 1;
}
CHECK(readBuffer != nullptr);
auto* rawAddr = reinterpret_cast<sockaddr*>(&addr);
rawAddr->sa_family = address().getFamily();
msg->msg_name = rawAddr;
msg->msg_namelen = kAddrLen;
#ifdef FOLLY_HAVE_MSG_ERRQUEUE
if (checkCmsgs) {
::memset(controlVec[i].data(), 0, controlVec[i].size());
msg->msg_control = controlVec[i].data();
msg->msg_controllen = controlVec[i].size();
}
#endif
}
// recvmmsg
int numMsgsRecvd = recvmmsg(msgs.data(), numPackets, flags, nullptr);
if (numMsgsRecvd < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Exit, socket will notify us again when socket is readable.
return RecvResult(NoReadReason::RETRIABLE_ERROR);
}
// If we got a non-retriable error, we might have received
// a packet that we could process, however let's just quit early.
pauseRead();
return RecvResult(NoReadReason::NONRETRIABLE_ERROR);
}
// process msgs (packets) returned by recvmmsg
CHECK_LE(numMsgsRecvd, numPackets);
for (uint16_t i = 0; i < static_cast<uint16_t>(numMsgsRecvd); ++i) {
auto& addr = recvmmsgStorage_.impl_[i].addr;
auto& readBuffer = recvmmsgStorage_.impl_[i].readBuffer;
auto& msg = msgs[i];
size_t bytesRead = msg.msg_len;
if (bytesRead == 0) {
// Empty datagram, this is probably garbage matching our tuple, we
// should ignore such datagrams.
continue;
}
QuicAsyncUDPSocket::ReadCallback::OnDataAvailableParams params;
#ifdef FOLLY_HAVE_MSG_ERRQUEUE
if (checkCmsgs) {
QuicAsyncUDPSocket::fromMsg(params, msg.msg_hdr);
// truncated
if (bytesRead > readBufferSize) {
bytesRead = readBufferSize;
if (params.gro > 0) {
bytesRead = bytesRead - bytesRead % params.gro;
}
}
}
#endif
totalData += bytesRead;
if (!peerAddress) {
peerAddress.emplace(folly::SocketAddress());
auto* rawAddr = reinterpret_cast<sockaddr*>(&addr);
peerAddress->setFromSockaddr(rawAddr, kAddrLen);
}
// timings
ReceivedUdpPacket::Timings timings;
// socket timestamps
//
// ts[0] -> software timestamp
// ts[1] -> hardware timestamp transformed to userspace time (deprecated)
// ts[2] -> hardware timestamp
if (params.ts.has_value()) {
const auto timespecToTimestamp = [](const timespec& ts)
-> Optional<ReceivedUdpPacket::Timings::SocketTimestampExt> {
std::chrono::nanoseconds duration = std::chrono::seconds(ts.tv_sec) +
std::chrono::nanoseconds(ts.tv_nsec);
if (duration == duration.zero()) {
return none;
}
ReceivedUdpPacket::Timings::SocketTimestampExt sockTsExt;
sockTsExt.rawDuration = duration;
sockTsExt.systemClock.raw = std::chrono::system_clock::time_point(
std::chrono::duration_cast<std::chrono::system_clock::duration>(
duration));
return sockTsExt;
};
const auto& ts = *params.ts;
timings.maybeSoftwareTs = timespecToTimestamp(ts[0]);
}
VLOG(10) << "Got data from socket peer=" << *peerAddress
<< " len=" << bytesRead;
readBuffer->append(bytesRead);
if (params.gro > 0) {
size_t len = bytesRead;
size_t remaining = len;
size_t offset = 0;
size_t totalNumPackets = networkData.getPackets().size() +
((len + params.gro - 1) / params.gro);
networkData.reserve(totalNumPackets);
while (remaining) {
if (static_cast<int>(remaining) > params.gro) {
auto tmp = readBuffer->cloneOne();
// start at offset
tmp->trimStart(offset);
// the actual len is len - offset now
// leave gro bytes
tmp->trimEnd(len - offset - params.gro);
DCHECK_EQ(tmp->length(), params.gro);
offset += params.gro;
remaining -= params.gro;
networkData.addPacket(
ReceivedUdpPacket(std::move(tmp), timings, params.tos));
} else {
// do not clone the last packet
// start at offset, use all the remaining data
readBuffer->trimStart(offset);
DCHECK_EQ(readBuffer->length(), remaining);
remaining = 0;
networkData.addPacket(
ReceivedUdpPacket(std::move(readBuffer), timings, params.tos));
}
}
} else {
networkData.addPacket(
ReceivedUdpPacket(std::move(readBuffer), timings, params.tos));
}
}
return {};
}
void QuicAsyncUDPSocketImpl::RecvmmsgStorage::resize(size_t numPackets) {
if (msgs.size() != numPackets) {
msgs.resize(numPackets);
impl_.resize(numPackets);
}
}
} // namespace quic