Passa al contenuto principale

6. Algoritmo (Algorithm)

6.1. Passi di inizializzazione (Initialization Steps)

All'inizio di una fase di risposta al controllo della congestione avviata da un algoritmo di controllo della congestione, un mittente di dati che utilizza PRR deve (deve) inizializzare lo stato PRR.

Il momento in cui inizia una fase di risposta al controllo della congestione è interamente determinato dall'algoritmo di controllo della congestione, ad esempio, può (può) corrispondere all'inizio di una fase di ripristino rapido, oppure può (può) verificarsi ogni giro in cui viene eseguita una riduzione dopo che il ripristino rapido è già in corso al rilevamento di una ritrasmissione persa o di una trasmissione originale persa.

L'inizializzazione PRR consente a un algoritmo di controllo della congestione CongCtrlAlg() di impostare ssthresh su un valore diverso da FlightSize/2 (ad esempio, incluso CUBIC [RFC9438]).

Un passo chiave nell'inizializzazione PRR è il calcolo della dimensione di volo di ripristino (Recovery Flight Size, RecoverFS), che è la stima del mittente del numero di byte che possono (può) essere consegnati durante la fase PRR corrente. Questo può (può) essere considerato come la somma dei seguenti valori all'inizio della fase: inflight, i byte riconosciuti cumulativamente nell'ACK che ha attivato il ripristino, i byte SACKati nell'ACK che ha attivato il ripristino e i byte già contrassegnati come persi tra SND.UNA e SND.NXT. RecoverFS include le perdite perché le perdite sono contrassegnate utilizzando euristiche e quindi durante il ripristino alcuni pacchetti precedentemente contrassegnati come persi possono (può) finire per essere consegnati (senza richiedere ritrasmissione). PRR utilizza RecoverFS per calcolare una velocità di invio fluida. All'ingresso nel ripristino rapido, PRR inizializza RecoverFS, e RecoverFS rimane costante per tutta una data fase di ripristino rapido.

La sequenza completa dei passi di inizializzazione dell'algoritmo PRR è la seguente:

ssthresh = CongCtrlAlg()  // Dimensione di volo target nel ripristino
prr_delivered = 0 // Totale byte consegnati nel ripristino
prr_out = 0 // Totale byte inviati nel ripristino
RecoverFS = SND.NXT - SND.UNA // Byte SACKati prima di entrare nel ripristino

// I byte SACKati prima di entrare nel ripristino non verranno contrassegnati
// come consegnati durante il ripristino:
RecoverFS -= (byte SACKati nello scoreboard)

// Includere i byte selettivamente ACKati (caso comune):
RecoverFS += (byte appena SACKati)

// Includere i byte riconosciuti cumulativamente (caso raro):
RecoverFS += (byte appena riconosciuti cumulativamente)

6.2. Passi per ACK (Per-ACK Steps)

Su ogni ACK all'inizio o durante il ripristino rapido, escludendo l'ACK che termina la fase PRR, PRR esegue i seguenti passi.

Prima di tutto, il mittente calcola DeliveredData, che è la migliore stima del mittente di dati del numero totale di byte che l'ACK corrente indica essere stati consegnati al ricevitore dall'ACK precedentemente ricevuto. Con SACK, DeliveredData può (può) essere calcolato con precisione come il cambiamento in SND.UNA, più il cambiamento con segno nella quantità di dati contrassegnati come SACKati nello scoreboard. Quindi, nel caso speciale in cui non ci sono intervalli di sequenze SACKati nello scoreboard prima o dopo un ACK, DeliveredData è il cambiamento in SND.UNA.

In un ripristino senza SACK, DeliveredData è stimato essere 1 SMSS su ogni ACK duplicato ricevuto (cioè, SND.UNA non è cambiato). Quando SND.UNA avanza (cioè, un ACK completo o parziale), DeliveredData è il cambiamento in SND.UNA, meno 1 SMSS per ogni ACK duplicato precedente. Si noti che senza SACK, un ricevitore che si comporta male che restituisce ACK duplicati in eccesso (come descritto in [Savage99]) potrebbe (potrebbe) cercare di gonfiare artificialmente DeliveredData. Come mitigazione, quando SACK non è in uso, PRR non consente di aumentare DeliveredData quando il totale dei byte consegnati nella fase PRR supera i dati in sospeso stimati all'ingresso nel ripristino (RecoverFS).

Successivamente, il mittente calcola inflight, che è la migliore stima del mittente di dati del numero di byte in volo nella rete. Per calcolare inflight, una connessione con SACK abilitato e che utilizza il rilevamento delle perdite [RFC6675] può (può) utilizzare l'algoritmo "pipe" specificato in [RFC6675]. Una connessione abilitata SACK che utilizza il rilevamento delle perdite RACK-TLP [RFC8985] o altri algoritmi di rilevamento delle perdite deve (deve) calcolare inflight iniziando con SND.NXT - SND.UNA, sottraendo i byte SACKati nello scoreboard, sottraendo i byte contrassegnati come persi nello scoreboard e aggiungendo i byte dallo scoreboard che sono stati ritrasmessi da quando sono stati contrassegnati come persi.

Per una connessione senza SACK abilitato, il mittente deve (deve) sottrarre: min(RecoverFS, 1 SMSS per ogni ACK duplicato precedente nella fase di ripristino rapido); il min() con RecoverFS serve a difendersi da un ricevitore che si comporta male [Savage99], invece di sottrarre i byte SACKati in uno scoreboard SACK.

Successivamente, il mittente calcola SafeACK, una variabile booleana locale che indica che l'ACK corrente segnala buoni progressi. SafeACK è vero solo quando l'ACK riconosce cumulativamente nuovi dati e l'ACK non indica ulteriori perdite. Ad esempio, un ACK che attiva una ritrasmissione di "salvataggio" (sezione 4 di [RFC6675], condizione 4 di NextSeg()) può (può) indicare ulteriori perdite. Entrambe queste condizioni indicano che il ripristino sta procedendo bene, e il mittente può (può) inviare più aggressivamente se appropriato, aumentando inflight.

Infine, il mittente utilizza DeliveredData, inflight, SafeACK e altri stati PRR per calcolare SndCnt, una variabile locale che indica quanti byte dovrebbero (dovrebbe) essere inviati in risposta a ciascun ACK, e quindi utilizza SndCnt per aggiornare cwnd.

La sequenza completa dei passi dell'algoritmo PRR per ACK è la seguente:

if (DeliveredData is 0)
Return

prr_delivered += DeliveredData
inflight = (quantità stimata di dati in volo)
SafeACK = (SND.UNA è avanzato E nessuna ulteriore perdita indicata)

if (inflight > ssthresh) {
// Riduzione proporzionale del tasso
// Questo utilizza la divisione intera, arrotondando per eccesso:
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
out = DIV_ROUND_UP(prr_delivered * ssthresh, RecoverFS)
SndCnt = out - prr_out
} else {
// Utilizzare PRR-CRB per impostazione predefinita
SndCnt = MAX(prr_delivered - prr_out, DeliveredData)
if (SafeACK) {
// Utilizzare PRR-SSRB quando il ripristino sta procedendo bene
SndCnt += SMSS
}
// Cercare di recuperare, per quanto consentito
SndCnt = MIN(ssthresh - inflight, SndCnt)
}

if (prr_out is 0 AND SndCnt is 0) {
// Forzare la ritrasmissione rapida all'ingresso nel ripristino
SndCnt = SMSS
}

cwnd = inflight + SndCnt

Dopo che il mittente ha calcolato SndCnt e lo ha utilizzato per aggiornare cwnd, il mittente trasmette più dati. Si noti che la decisione su quali dati inviare (ad esempio, ritrasmettere dati mancanti o inviare più nuovi dati) è al di fuori dell'ambito di questo documento.

6.3. Passi per trasmissione (Per-Transmit Steps)

Su qualsiasi trasmissione o ritrasmissione di dati, PRR esegue quanto segue:

prr_out += (dati inviati)

6.4. Passi di completamento (Completion Steps)

La fase PRR termina al completamento del ripristino rapido o prima di iniziare una nuova fase PRR a causa di una nuova fase di risposta al controllo della congestione.

Al completamento di una fase PRR, PRR esegue quanto segue:

cwnd = ssthresh

Si noti che questo passo per impostare cwnd su ssthresh può (può) in alcuni casi consentire una raffica consecutiva di segmenti nella rete.

Le implementazioni sono incoraggiate (raccomandate) a utilizzare il pacing per ridurre la raffica del flusso di dati. Questo incoraggiamento è coerente con le pratiche attuali per mitigare le raffiche (ad esempio, [PACING]), incluso il pacing delle raffiche di trasmissione dopo il riavvio dall'inattività.