6. Algorithme (Algorithm)
6.1. Étapes d'initialisation (Initialization Steps)
Au début d'une phase de réponse au contrôle de congestion initiée par un algorithme de contrôle de congestion, un expéditeur de données utilisant PRR doit (doit) initialiser l'état PRR.
Le moment où une phase de réponse au contrôle de congestion commence est entièrement déterminé par l'algorithme de contrôle de congestion, par exemple, il peut (peut) correspondre au début d'une phase de récupération rapide, ou il peut (peut) se produire à chaque tour où une réduction est effectuée après que la récupération rapide est déjà en cours lors de la détection d'une retransmission perdue ou d'une transmission originale perdue.
L'initialisation PRR permet à un algorithme de contrôle de congestion CongCtrlAlg() de définir ssthresh à une valeur autre que FlightSize/2 (par exemple, incluant CUBIC [RFC9438]).
Une étape clé de l'initialisation PRR est le calcul de la taille de vol de récupération (Recovery Flight Size, RecoverFS), qui est l'estimation de l'expéditeur du nombre d'octets qui peuvent (peut) être livrés pendant la phase PRR actuelle. Cela peut (peut) être considéré comme la somme des valeurs suivantes au début de la phase : inflight, les octets acquittés de manière cumulative dans l'ACK qui a déclenché la récupération, les octets SACKés dans l'ACK qui a déclenché la récupération, et les octets déjà marqués comme perdus entre SND.UNA et SND.NXT. RecoverFS inclut les pertes car les pertes sont marquées à l'aide d'heuristiques et donc pendant la récupération, certains paquets précédemment marqués comme perdus peuvent (peut) finalement être livrés (sans nécessiter de retransmission). PRR utilise RecoverFS pour calculer un débit d'envoi fluide. Lors de l'entrée en récupération rapide, PRR initialise RecoverFS, et RecoverFS reste constant tout au long d'une phase de récupération rapide donnée.
La séquence complète des étapes d'initialisation de l'algorithme PRR est la suivante :
ssthresh = CongCtrlAlg() // Taille de vol cible dans la récupération
prr_delivered = 0 // Total d'octets livrés dans la récupération
prr_out = 0 // Total d'octets envoyés dans la récupération
RecoverFS = SND.NXT - SND.UNA // Octets SACKés avant d'entrer en récupération
// Les octets SACKés avant d'entrer en récupération ne seront pas marqués
// comme livrés pendant la récupération :
RecoverFS -= (octets SACKés dans le scoreboard)
// Inclure les octets sélectivement ACKés (cas courant) :
RecoverFS += (octets nouvellement SACKés)
// Inclure les octets acquittés de manière cumulative (cas rare) :
RecoverFS += (octets nouvellement acquittés de manière cumulative)
6.2. Étapes par ACK (Per-ACK Steps)
Sur chaque ACK au début ou pendant la récupération rapide, à l'exception de l'ACK qui termine la phase PRR, PRR effectue les étapes suivantes.
Tout d'abord, l'expéditeur calcule DeliveredData, qui est la meilleure estimation de l'expéditeur de données du nombre total d'octets que l'ACK actuel indique avoir été livrés au récepteur depuis l'ACK précédemment reçu. Avec SACK, DeliveredData peut (peut) être calculé précisément comme le changement dans SND.UNA, plus le changement signé dans la quantité de données marquées SACKées dans le scoreboard. Ainsi, dans le cas spécial où il n'y a pas de plages de séquences SACKées dans le scoreboard avant ou après un ACK, DeliveredData est le changement dans SND.UNA.
Dans une récupération sans SACK, DeliveredData est estimé à 1 SMSS sur chaque ACK dupliqué reçu (c'est-à-dire que SND.UNA n'a pas changé). Lorsque SND.UNA avance (c'est-à-dire un ACK complet ou partiel), DeliveredData est le changement dans SND.UNA, moins 1 SMSS pour chaque ACK dupliqué précédent. Notez que sans SACK, un récepteur mal comporté qui renvoie des ACK dupliqués excédentaires (comme décrit dans [Savage99]) pourrait (pourrait) essayer de gonfler artificiellement DeliveredData. Comme mesure d'atténuation, lorsque SACK n'est pas utilisé, PRR ne permet pas d'augmenter DeliveredData lorsque le total d'octets livrés dans la phase PRR dépasse les données en suspens estimées lors de l'entrée en récupération (RecoverFS).
Ensuite, l'expéditeur calcule inflight, qui est la meilleure estimation de l'expéditeur de données du nombre d'octets en vol dans le réseau. Pour calculer inflight, une connexion avec SACK activé et utilisant la détection de perte [RFC6675] peut (peut) utiliser l'algorithme "pipe" spécifié dans [RFC6675]. Une connexion compatible SACK utilisant la détection de perte RACK-TLP [RFC8985] ou d'autres algorithmes de détection de perte doit (doit) calculer inflight en commençant par SND.NXT - SND.UNA, en soustrayant les octets SACKés dans le scoreboard, en soustrayant les octets marqués comme perdus dans le scoreboard, et en ajoutant les octets du scoreboard qui ont été retransmis depuis qu'ils ont été marqués comme perdus.
Pour une connexion sans SACK activé, l'expéditeur doit (doit) soustraire : min(RecoverFS, 1 SMSS pour chaque ACK dupliqué précédent dans la phase de récupération rapide) ; le min() avec RecoverFS est pour se défendre contre un récepteur mal comporté [Savage99], au lieu de soustraire les octets SACKés dans un scoreboard SACK.
Ensuite, l'expéditeur calcule SafeACK, une variable booléenne locale indiquant que l'ACK actuel rapporte une bonne progression. SafeACK est vrai uniquement lorsque l'ACK acquitte de manière cumulative de nouvelles données et que l'ACK n'indique pas de pertes supplémentaires. Par exemple, un ACK qui déclenche une retransmission de "secours" (section 4 de [RFC6675], condition 4 de NextSeg()) peut (peut) indiquer des pertes supplémentaires. Ces deux conditions indiquent que la récupération progresse bien, et l'expéditeur peut (peut) envoyer plus agressivement si approprié, augmentant inflight.
Enfin, l'expéditeur utilise DeliveredData, inflight, SafeACK et d'autres états PRR pour calculer SndCnt, une variable locale indiquant combien d'octets devraient (devrait) être envoyés en réponse à chaque ACK, puis utilise SndCnt pour mettre à jour cwnd.
La séquence complète des étapes de l'algorithme PRR par ACK est la suivante :
if (DeliveredData is 0)
Return
prr_delivered += DeliveredData
inflight = (quantité estimée de données en vol)
SafeACK = (SND.UNA a avancé ET aucune perte supplémentaire indiquée)
if (inflight > ssthresh) {
// Réduction proportionnelle du débit
// Ceci utilise la division entière, en arrondissant vers le haut :
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
out = DIV_ROUND_UP(prr_delivered * ssthresh, RecoverFS)
SndCnt = out - prr_out
} else {
// Utiliser PRR-CRB par défaut
SndCnt = MAX(prr_delivered - prr_out, DeliveredData)
if (SafeACK) {
// Utiliser PRR-SSRB lorsque la récupération progresse bien
SndCnt += SMSS
}
// Essayer de rattraper, autant que permis
SndCnt = MIN(ssthresh - inflight, SndCnt)
}
if (prr_out is 0 AND SndCnt is 0) {
// Forcer la retransmission rapide lors de l'entrée en récupération
SndCnt = SMSS
}
cwnd = inflight + SndCnt
Après que l'expéditeur ait calculé SndCnt et l'ait utilisé pour mettre à jour cwnd, l'expéditeur transmet plus de données. Notez que la décision de quelles données envoyer (par exemple, retransmettre des données manquantes ou envoyer plus de nouvelles données) est au-delà de la portée de ce document.
6.3. Étapes par transmission (Per-Transmit Steps)
Lors de toute transmission ou retransmission de données, PRR effectue ce qui suit :
prr_out += (données envoyées)
6.4. Étapes de finalisation (Completion Steps)
La phase PRR se termine lors de l'achèvement de la récupération rapide ou avant le démarrage d'une nouvelle phase PRR en raison d'une nouvelle phase de réponse au contrôle de congestion.
Lors de l'achèvement d'une phase PRR, PRR effectue ce qui suit :
cwnd = ssthresh
Notez que cette étape pour définir cwnd à ssthresh peut (peut) dans certains cas permettre une rafale consécutive de segments dans le réseau.
Les implémentations sont encouragées (encouragées) à utiliser le pacing pour réduire le caractère en rafale du flux de données. Cet encouragement est cohérent avec les pratiques actuelles pour atténuer les rafales (par exemple, [PACING]), y compris le pacing des rafales de transmission après le redémarrage à partir de l'état inactif.