1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-01 01:44:22 +03:00

Clone and schedule all packets containing CRYPTO frames

Summary:
A packet in the CloningScheduler can be in three states:

1. Not cloned yet
2. Cloned, not processed yet
3. Cloned and processed

Currently, CloningScheduler will clone a packet in state 1, moving it to state 2, and continue cloning the packet while it is in state 2 without proceeding to subsequent packets.

This change makes it so that a packet in state 2 that has a CRYPTO frame will be skipped over, so that the next packet (which will be in state 1) has a chance to also be cloned.

Extra: Fix `DoNotCloneProcessedClonedPacket` to have the first packet be the already-processed one. If the second packet is the only one already processed, then of course the first one would be scheduled, letting the test speciously pass.

Reviewed By: mjoras

Differential Revision: D62770483

fbshipit-source-id: 5b00958ab4a787615338debacbe9bd2a0f6f74a4
This commit is contained in:
Jolene Tan
2024-09-23 13:09:46 -07:00
committed by Facebook GitHub Bot
parent 8c62142b55
commit c2715ab822
3 changed files with 130 additions and 29 deletions

View File

@ -918,15 +918,25 @@ SchedulingResult CloningScheduler::scheduleFramesForPacket(
header,
getAckState(conn_, builderPnSpace).largestAckedByPeer.value_or(0));
}
// If the packet is already a clone that has been processed, we don't clone
// it again.
if (outstandingPacket.maybeClonedPacketIdentifier &&
conn_.outstandings.clonedPacketIdentifiers.count(
*outstandingPacket.maybeClonedPacketIdentifier) == 0) {
continue;
// The packet is already a clone
if (outstandingPacket.maybeClonedPacketIdentifier) {
// If packet has CRYPTO frame, don't clone it again even if not processed
// yet, move on to give next packet a chance to be cloned
const auto& frames = outstandingPacket.packet.frames;
if (conn_.transportSettings.cloneAllPacketsWithCryptoFrame &&
std::find_if(frames.begin(), frames.end(), [](const auto& frame) {
return frame.type() == QuicWriteFrame::Type::WriteCryptoFrame;
}) != frames.end()) {
continue;
}
// Otherwise, clone until it is processed
if (conn_.outstandings.clonedPacketIdentifiers.count(
*outstandingPacket.maybeClonedPacketIdentifier) == 0) {
continue;
}
}
// I think this only fail if udpSendPacketLen somehow shrinks in the middle
// of a connection.
// I think this only fail if udpSendPacketLen somehow shrinks in the
// middle of a connection.
if (outstandingPacket.metadata.encodedSize >
writableBytes + cipherOverhead_) {
continue;
@ -936,13 +946,14 @@ SchedulingResult CloningScheduler::scheduleFramesForPacket(
internalBuilder->encodePacketHeader();
PacketRebuilder rebuilder(*internalBuilder, conn_);
// TODO: It's possible we write out a packet that's larger than the packet
// size limit. For example, when the packet sequence number has advanced to
// a point where we need more bytes to encoded it than that of the original
// packet. In that case, if the original packet is already at the packet
// size limit, we will generate a packet larger than the limit. We can
// either ignore the problem, hoping the packet will be able to travel the
// network just fine; Or we can throw away the built packet and send a ping.
// TODO: It's possible we write out a packet that's larger than the
// packet size limit. For example, when the packet sequence number
// has advanced to a point where we need more bytes to encoded it
// than that of the original packet. In that case, if the original
// packet is already at the packet size limit, we will generate a
// packet larger than the limit. We can either ignore the problem,
// hoping the packet will be able to travel the network just fine;
// Or we can throw away the built packet and send a ping.
// Rebuilder will write the rest of frames
auto rebuildResult = rebuilder.rebuildFromPacket(outstandingPacket);
@ -954,14 +965,15 @@ SchedulingResult CloningScheduler::scheduleFramesForPacket(
} else if (
conn_.transportSettings.dataPathType ==
DataPathType::ContinuousMemory) {
// When we use Inplace packet building and reuse the write buffer, even if
// the packet rebuild has failed, there might be some bytes already
// written into the buffer and the buffer tail pointer has already moved.
// We need to roll back the tail pointer to the position before the packet
// building to exclude those bytes. Otherwise these bytes will be sitting
// in between legit packets inside the buffer and will either cause errors
// further down the write path, or be sent out and then dropped at peer
// when peer fail to parse them.
// When we use Inplace packet building and reuse the write buffer,
// even if the packet rebuild has failed, there might be some
// bytes already written into the buffer and the buffer tail
// pointer has already moved. We need to roll back the tail
// pointer to the position before the packet building to exclude
// those bytes. Otherwise these bytes will be sitting in between
// legit packets inside the buffer and will either cause errors
// further down the write path, or be sent out and then dropped at
// peer when peer fail to parse them.
internalBuilder.reset();
CHECK(conn_.bufAccessor && conn_.bufAccessor->ownsBuffer());
conn_.bufAccessor->trimEnd(conn_.bufAccessor->length() - prevSize);