Merge pull request #2218 from lucas-clemente/pto-per-pn-space

change PTO to be per packet number space
This commit is contained in:
Marten Seemann
2019-11-22 18:35:05 +08:00
committed by GitHub
18 changed files with 492 additions and 241 deletions

View File

@@ -25,7 +25,9 @@ type packetNumberSpace struct {
history *sentPacketHistory
pns *packetNumberGenerator
lossTime time.Time
lossTime time.Time
lastSentAckElicitingPacketTime time.Time
largestAcked protocol.PacketNumber
largestSent protocol.PacketNumber
}
@@ -40,15 +42,14 @@ func newPacketNumberSpace(initialPN protocol.PacketNumber) *packetNumberSpace {
}
type sentPacketHandler struct {
lastSentAckElicitingPacketTime time.Time // only applies to the application-data packet number space
lastSentCryptoPacketTime time.Time
nextSendTime time.Time
initialPackets *packetNumberSpace
handshakePackets *packetNumberSpace
oneRTTPackets *packetNumberSpace
handshakeComplete bool
// lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived
// example: we send an ACK for packets 90-100 with packet number 20
// once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101
@@ -62,6 +63,7 @@ type sentPacketHandler struct {
// The number of times a PTO has been sent without receiving an ack.
ptoCount uint32
ptoMode SendMode
// The number of PTO probe packets that should be sent.
// Only applies to the application-data packet number space.
numProbesToSend int
@@ -153,10 +155,7 @@ func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* is ack-elicit
isAckEliciting := len(packet.Frames) > 0
if isAckEliciting {
if packet.EncryptionLevel != protocol.Encryption1RTT {
h.lastSentCryptoPacketTime = packet.SendTime
}
h.lastSentAckElicitingPacketTime = packet.SendTime
pnSpace.lastSentAckElicitingPacketTime = packet.SendTime
packet.includedInBytesInFlight = true
h.bytesInFlight += packet.Length
if h.numProbesToSend > 0 {
@@ -281,7 +280,7 @@ func (h *sentPacketHandler) determineNewlyAckedPackets(
return ackedPackets, err
}
func (h *sentPacketHandler) getEarliestLossTime() (time.Time, protocol.EncryptionLevel) {
func (h *sentPacketHandler) getEarliestLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
var encLevel protocol.EncryptionLevel
var lossTime time.Time
@@ -293,13 +292,35 @@ func (h *sentPacketHandler) getEarliestLossTime() (time.Time, protocol.Encryptio
lossTime = h.handshakePackets.lossTime
encLevel = protocol.EncryptionHandshake
}
if lossTime.IsZero() || (!h.oneRTTPackets.lossTime.IsZero() && h.oneRTTPackets.lossTime.Before(lossTime)) {
if h.handshakeComplete &&
(lossTime.IsZero() || (!h.oneRTTPackets.lossTime.IsZero() && h.oneRTTPackets.lossTime.Before(lossTime))) {
lossTime = h.oneRTTPackets.lossTime
encLevel = protocol.Encryption1RTT
}
return lossTime, encLevel
}
// same logic as getEarliestLossTimeAndSpace, but for lastSentAckElicitingPacketTime instead of lossTime
func (h *sentPacketHandler) getEarliestSentTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
var encLevel protocol.EncryptionLevel
var sentTime time.Time
if h.initialPackets != nil {
sentTime = h.initialPackets.lastSentAckElicitingPacketTime
encLevel = protocol.EncryptionInitial
}
if h.handshakePackets != nil && (sentTime.IsZero() || (!h.handshakePackets.lastSentAckElicitingPacketTime.IsZero() && h.handshakePackets.lastSentAckElicitingPacketTime.Before(sentTime))) {
sentTime = h.handshakePackets.lastSentAckElicitingPacketTime
encLevel = protocol.EncryptionHandshake
}
if h.handshakeComplete &&
(sentTime.IsZero() || (!h.oneRTTPackets.lastSentAckElicitingPacketTime.IsZero() && h.oneRTTPackets.lastSentAckElicitingPacketTime.Before(sentTime))) {
sentTime = h.oneRTTPackets.lastSentAckElicitingPacketTime
encLevel = protocol.Encryption1RTT
}
return sentTime, encLevel
}
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
var hasInitial, hasHandshake bool
if h.initialPackets != nil {
@@ -312,11 +333,14 @@ func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
}
func (h *sentPacketHandler) hasOutstandingPackets() bool {
return h.oneRTTPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
// We only send application data probe packets once the handshake completes,
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
return (h.handshakeComplete && h.oneRTTPackets.history.HasOutstandingPackets()) ||
h.hasOutstandingCryptoPackets()
}
func (h *sentPacketHandler) setLossDetectionTimer() {
if lossTime, _ := h.getEarliestLossTime(); !lossTime.IsZero() {
if lossTime, _ := h.getEarliestLossTimeAndSpace(); !lossTime.IsZero() {
// Early retransmit timer or time loss detection.
h.alarm = lossTime
}
@@ -329,7 +353,8 @@ func (h *sentPacketHandler) setLossDetectionTimer() {
}
// PTO alarm
h.alarm = h.lastSentAckElicitingPacketTime.Add(h.rttStats.PTO() << h.ptoCount)
sentTime, encLevel := h.getEarliestSentTimeAndSpace()
h.alarm = sentTime.Add(h.rttStats.PTO(encLevel == protocol.Encryption1RTT) << h.ptoCount)
}
func (h *sentPacketHandler) detectLostPackets(
@@ -405,7 +430,7 @@ func (h *sentPacketHandler) detectLostPackets(
func (h *sentPacketHandler) OnLossDetectionTimeout() error {
// When all outstanding are acknowledged, the alarm is canceled in
// updateLossDetectionAlarm. This doesn't reset the timer in the session though.
// setLossDetectionTimer. This doesn't reset the timer in the session though.
// When OnAlarm is called, we therefore need to make sure that there are
// actually packets outstanding.
if h.hasOutstandingPackets() {
@@ -418,10 +443,10 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
}
func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
lossTime, encLevel := h.getEarliestLossTime()
if !lossTime.IsZero() {
earliestLossTime, encLevel := h.getEarliestLossTimeAndSpace()
if !earliestLossTime.IsZero() {
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", lossTime)
h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime)
}
// Early retransmit or time loss detection
return h.detectLostPackets(time.Now(), encLevel, h.bytesInFlight)
@@ -429,10 +454,21 @@ func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
// PTO
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm fired in PTO mode. PTO count: %d", h.ptoCount)
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
}
_, encLevel = h.getEarliestSentTimeAndSpace()
h.ptoCount++
h.numProbesToSend += 2
switch encLevel {
case protocol.EncryptionInitial:
h.ptoMode = SendPTOInitial
case protocol.EncryptionHandshake:
h.ptoMode = SendPTOHandshake
case protocol.Encryption1RTT:
h.ptoMode = SendPTOAppData
default:
return fmt.Errorf("TPO timer in unexpected encryption level: %s", encLevel)
}
return nil
}
@@ -495,7 +531,7 @@ func (h *sentPacketHandler) SendMode() SendMode {
return SendNone
}
if h.numProbesToSend > 0 {
return SendPTO
return h.ptoMode
}
// Only send ACKs if we're congestion limited.
if !h.congestion.CanSend(h.bytesInFlight) {
@@ -529,17 +565,9 @@ func (h *sentPacketHandler) ShouldSendNumPackets() int {
return int(math.Ceil(float64(protocol.MinPacingDelay) / float64(delay)))
}
func (h *sentPacketHandler) QueueProbePacket() bool {
var p *Packet
if h.initialPackets != nil {
p = h.initialPackets.history.FirstOutstanding()
}
if p == nil && h.handshakePackets != nil {
p = h.handshakePackets.history.FirstOutstanding()
}
if p == nil {
p = h.oneRTTPackets.history.FirstOutstanding()
}
func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) bool {
pnSpace := h.getPacketNumberSpace(encLevel)
p := pnSpace.history.FirstOutstanding()
if p == nil {
return false
}
@@ -549,8 +577,8 @@ func (h *sentPacketHandler) QueueProbePacket() bool {
if p.includedInBytesInFlight {
h.bytesInFlight -= p.Length
}
if err := h.getPacketNumberSpace(p.EncryptionLevel).history.Remove(p.PacketNumber); err != nil {
// should never happen. We just got this packet from the history a lines above.
if err := pnSpace.history.Remove(p.PacketNumber); err != nil {
// should never happen. We just got this packet from the history.
panic(err)
}
return true
@@ -573,6 +601,13 @@ func (h *sentPacketHandler) ResetForRetry() error {
return nil
}
func (h *sentPacketHandler) SetHandshakeComplete() {
h.handshakeComplete = true
// We don't send PTOs for application data packets before the handshake completes.
// Make sure the timer is armed now, if necessary.
h.setLossDetectionTimer()
}
func (h *sentPacketHandler) GetStats() *quictrace.TransportState {
return &quictrace.TransportState{
MinRTT: h.rttStats.MinRTT(),