changeset 8823:f3331deed357 quic

QUIC: limit in-flight bytes by congestion window. Previously, in-flight byte counter and congestion window were properly maintained, but the limit was not properly implemented. Now a new datagram is sent only if in-flight byte counter is less than window. The limit is datagram-based, which means that a single datagram may lead to exceeding the limit, but the next one will not be sent.
author Roman Arutyunyan <arut@nginx.com>
date Thu, 29 Jul 2021 12:49:16 +0300
parents ad046179eb91
children 054f9be0aaf9
files src/event/quic/ngx_event_quic_ack.c src/event/quic/ngx_event_quic_output.c
diffstat 2 files changed, 27 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/quic/ngx_event_quic_ack.c
+++ b/src/event/quic/ngx_event_quic_ack.c
@@ -293,6 +293,7 @@ ngx_quic_handle_ack_frame_range(ngx_conn
 void
 ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
 {
+    ngx_uint_t              blocked;
     ngx_msec_t              timer;
     ngx_quic_congestion_t  *cg;
     ngx_quic_connection_t  *qc;
@@ -304,6 +305,8 @@ ngx_quic_congestion_ack(ngx_connection_t
     qc = ngx_quic_get_connection(c);
     cg = &qc->congestion;
 
+    blocked = (cg->in_flight >= cg->window) ? 1 : 0;
+
     cg->in_flight -= f->plen;
 
     timer = f->last - cg->recovery_start;
@@ -313,7 +316,7 @@ ngx_quic_congestion_ack(ngx_connection_t
                        "quic congestion ack recovery win:%uz ss:%z if:%uz",
                        cg->window, cg->ssthresh, cg->in_flight);
 
-        return;
+        goto done;
     }
 
     if (cg->window < cg->ssthresh) {
@@ -338,6 +341,12 @@ ngx_quic_congestion_ack(ngx_connection_t
     if ((ngx_msec_int_t) timer < 0) {
         cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
     }
+
+done:
+
+    if (blocked && cg->in_flight < cg->window) {
+        ngx_post_event(&qc->push, &ngx_posted_events);
+    }
 }
 
 
@@ -620,6 +629,7 @@ ngx_quic_resend_frames(ngx_connection_t 
 static void
 ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
 {
+    ngx_uint_t              blocked;
     ngx_msec_t              timer;
     ngx_quic_congestion_t  *cg;
     ngx_quic_connection_t  *qc;
@@ -631,6 +641,8 @@ ngx_quic_congestion_lost(ngx_connection_
     qc = ngx_quic_get_connection(c);
     cg = &qc->congestion;
 
+    blocked = (cg->in_flight >= cg->window) ? 1 : 0;
+
     cg->in_flight -= f->plen;
     f->plen = 0;
 
@@ -641,7 +653,7 @@ ngx_quic_congestion_lost(ngx_connection_
                        "quic congestion lost recovery win:%uz ss:%z if:%uz",
                        cg->window, cg->ssthresh, cg->in_flight);
 
-        return;
+        goto done;
     }
 
     cg->recovery_start = ngx_current_msec;
@@ -656,6 +668,12 @@ ngx_quic_congestion_lost(ngx_connection_
     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "quic congestion lost win:%uz ss:%z if:%uz",
                    cg->window, cg->ssthresh, cg->in_flight);
+
+done:
+
+    if (blocked && cg->in_flight < cg->window) {
+        ngx_post_event(&qc->push, &ngx_posted_events);
+    }
 }
 
 
--- a/src/event/quic/ngx_event_quic_output.c
+++ b/src/event/quic/ngx_event_quic_output.c
@@ -147,13 +147,16 @@ ngx_quic_create_datagrams(ngx_connection
     ngx_uint_t              i, pad;
     ngx_quic_path_t        *path;
     ngx_quic_send_ctx_t    *ctx;
+    ngx_quic_congestion_t  *cg;
     ngx_quic_connection_t  *qc;
     static u_char           dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
 
     qc = ngx_quic_get_connection(c);
+    cg = &qc->congestion;
     path = qsock->path;
 
-    for ( ;; ) {
+    while (cg->in_flight < cg->window) {
+
         p = dst;
 
         len = ngx_min(qc->ctp.max_udp_payload_size,
@@ -339,10 +342,12 @@ ngx_quic_create_segments(ngx_connection_
     ngx_uint_t              nseg;
     ngx_quic_path_t        *path;
     ngx_quic_send_ctx_t    *ctx;
+    ngx_quic_congestion_t  *cg;
     ngx_quic_connection_t  *qc;
     static u_char           dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];
 
     qc = ngx_quic_get_connection(c);
+    cg = &qc->congestion;
     path = qsock->path;
 
     ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
@@ -364,7 +369,7 @@ ngx_quic_create_segments(ngx_connection_
 
         len = ngx_min(segsize, (size_t) (end - p));
 
-        if (len) {
+        if (len && cg->in_flight < cg->window) {
 
             n = ngx_quic_output_packet(c, ctx, p, len, len, qsock);
             if (n == NGX_ERROR) {
@@ -531,7 +536,6 @@ ngx_quic_output_packet(ngx_connection_t 
     ngx_queue_t            *q;
     ngx_quic_frame_t       *f;
     ngx_quic_header_t       pkt;
-    ngx_quic_congestion_t  *cg;
     ngx_quic_connection_t  *qc;
     static u_char           src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
 
@@ -545,7 +549,6 @@ ngx_quic_output_packet(ngx_connection_t 
                    max, min);
 
     qc = ngx_quic_get_connection(c);
-    cg = &qc->congestion;
 
     hlen = (ctx->level == ssl_encryption_application)
            ? NGX_QUIC_MAX_SHORT_HEADER
@@ -568,10 +571,6 @@ ngx_quic_output_packet(ngx_connection_t 
     {
         f = ngx_queue_data(q, ngx_quic_frame_t, queue);
 
-        if (!pkt.need_ack && f->need_ack && max > cg->window) {
-            max = cg->window;
-        }
-
         if (f->type == NGX_QUIC_FT_PATH_RESPONSE
             || f->type == NGX_QUIC_FT_PATH_CHALLENGE)
         {