changeset 8513:38f1be6c603f quic

QUIC: sending probe packets on PTO timer expiration. The PTO handler is split into separate PTO and loss detection handlers that operate interchangeably depending on which timer should be set. The present ngx_quic_lost_handler is now only used for packet loss detection. It replaces ngx_quic_pto_handler if there are packets preceeding largest_ack. Once there is no more such packets, ngx_quic_pto_handler is installed again. Probes carry unacknowledged data previously sent in the oldest packet number, one per each packet number space. That is, it could be up to two probes. PTO backoff is now increased before scheduling next probes.
author Sergey Kandaurov <pluknet@nginx.com>
date Wed, 19 Aug 2020 13:24:23 +0300
parents 6e1c88f82280
children 52c88f41d24c
files src/event/ngx_event_quic.c
diffstat 1 files changed, 115 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -265,7 +265,10 @@ static ngx_int_t ngx_quic_send_frames(ng
 static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt,
     ngx_quic_send_ctx_t *ctx);
 static void ngx_quic_pto_handler(ngx_event_t *ev);
-static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c, ngx_uint_t ack);
+static void ngx_quic_lost_handler(ngx_event_t *ev);
+static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c);
+static ngx_int_t ngx_quic_resend_frames(ngx_connection_t *c,
+    ngx_quic_send_ctx_t *ctx, ngx_quic_frame_t *start);
 static void ngx_quic_push_handler(ngx_event_t *ev);
 
 static void ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp,
@@ -2401,7 +2404,7 @@ ngx_quic_handle_ack_frame(ngx_connection
         }
     }
 
-    return ngx_quic_detect_lost(c, 1);
+    return ngx_quic_detect_lost(c);
 }
 
 
@@ -3682,18 +3685,46 @@ ngx_quic_set_packet_number(ngx_quic_head
 static void
 ngx_quic_pto_handler(ngx_event_t *ev)
 {
-    ngx_connection_t  *c;
+    ngx_uint_t              i;
+    ngx_queue_t            *q;
+    ngx_connection_t       *c;
+    ngx_quic_frame_t       *start;
+    ngx_quic_send_ctx_t    *ctx;
+    ngx_quic_connection_t  *qc;
 
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic pto timer");
 
     c = ev->data;
-
-    if (ngx_quic_detect_lost(c, 0) != NGX_OK) {
-        ngx_quic_close_connection(c, NGX_ERROR);
-        return;
-    }
-
-    c->quic->pto_count++;
+    qc = c->quic;
+
+    qc->pto_count++;
+
+    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
+
+        ctx = &qc->send_ctx[i];
+
+        if (ngx_queue_empty(&ctx->sent)) {
+            continue;
+        }
+
+        q = ngx_queue_head(&ctx->sent);
+        start = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+        if (start->pnum <= ctx->largest_ack
+            && ctx->largest_ack != (uint64_t) -1)
+        {
+            continue;
+        }
+
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic pto pnum:%ui pto_count:%ui level:%d",
+                       start->pnum, c->quic->pto_count, start->level);
+
+        if (ngx_quic_resend_frames(c, ctx, start) != NGX_OK) {
+            ngx_quic_close_connection(c, NGX_ERROR);
+            return;
+        }
+    }
 }
 
 
@@ -3713,14 +3744,28 @@ ngx_quic_push_handler(ngx_event_t *ev)
 }
 
 
+static
+void ngx_quic_lost_handler(ngx_event_t *ev)
+{
+    ngx_connection_t  *c;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic lost timer");
+
+    c = ev->data;
+
+    if (ngx_quic_detect_lost(c) != NGX_OK) {
+        ngx_quic_close_connection(c, NGX_ERROR);
+    }
+}
+
+
 static ngx_int_t
-ngx_quic_detect_lost(ngx_connection_t *c, ngx_uint_t ack)
+ngx_quic_detect_lost(ngx_connection_t *c)
 {
-    uint64_t                pn;
     ngx_uint_t              i;
     ngx_msec_t              now, wait, min_wait, thr;
-    ngx_queue_t            *q, range;
-    ngx_quic_frame_t       *f, *start;
+    ngx_queue_t            *q;
+    ngx_quic_frame_t       *start;
     ngx_quic_send_ctx_t    *ctx;
     ngx_quic_connection_t  *qc;
 
@@ -3729,91 +3774,96 @@ ngx_quic_detect_lost(ngx_connection_t *c
 
     min_wait = 0;
 
-#if (NGX_SUPPRESS_WARN)
-    thr = 0;
-#endif
-
-    if (ack) {
-        thr = NGX_QUIC_TIME_THR * ngx_max(qc->latest_rtt, qc->avg_rtt);
-        thr = ngx_max(thr, NGX_QUIC_TIME_GRANULARITY);
-    }
+    thr = NGX_QUIC_TIME_THR * ngx_max(qc->latest_rtt, qc->avg_rtt);
+    thr = ngx_max(thr, NGX_QUIC_TIME_GRANULARITY);
 
     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
 
         ctx = &qc->send_ctx[i];
 
-        if (ngx_queue_empty(&ctx->sent)) {
-            continue;
-        }
-
-        if (!ack) {
-            thr = ngx_quic_pto(c, ctx);
-        }
-
-        q = ngx_queue_head(&ctx->sent);
-
-        do {
+        while (!ngx_queue_empty(&ctx->sent)) {
+
+            q = ngx_queue_head(&ctx->sent);
             start = ngx_queue_data(q, ngx_quic_frame_t, queue);
 
             wait = start->last + thr - now;
 
+            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "quic detect_lost pnum:%ui thr:%M wait:%i level:%d",
+                           start->pnum, thr, (ngx_int_t) wait, start->level);
+
             if ((ngx_msec_int_t) wait >= 0) {
 
                 if (min_wait == 0 || wait < min_wait) {
                     min_wait = wait;
                 }
 
-                if (!ack) {
-                    break;
-                }
-
                 if ((start->pnum > ctx->largest_ack)
+                     || ctx->largest_ack == (uint64_t) -1
                      || ((ctx->largest_ack - start->pnum) < NGX_QUIC_PKT_THR))
                 {
                     break;
                 }
             }
 
-            pn = start->pnum;
-
-            ngx_queue_init(&range);
-
-            /* send frames with same packet number to the wire */
-            do {
-                f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
-                if (f->pnum != pn) {
-                    break;
-                }
-
-                q = ngx_queue_next(q);
-
-                ngx_queue_remove(&f->queue);
-                ngx_queue_insert_tail(&range, &f->queue);
-
-            } while (q != ngx_queue_sentinel(&ctx->sent));
-
-            ngx_quic_congestion_lost(c, start);
-
-            if (ngx_quic_send_frames(c, ctx, &range) != NGX_OK) {
+            if (ngx_quic_resend_frames(c, ctx, start) != NGX_OK) {
                 return NGX_ERROR;
             }
-
-        } while (q != ngx_queue_sentinel(&ctx->sent));
-    }
+        }
+    }
+
+    /* no more preceeding packets */
+
+    if (min_wait == 0) {
+        qc->pto.handler = ngx_quic_pto_handler;
+        return NGX_OK;
+    }
+
+    qc->pto.handler = ngx_quic_lost_handler;
 
     if (qc->pto.timer_set) {
         ngx_del_timer(&qc->pto);
     }
 
-    if (min_wait > 0) {
-        ngx_add_timer(&qc->pto, min_wait);
-    }
+    ngx_add_timer(&qc->pto, min_wait);
 
     return NGX_OK;
 }
 
 
+static ngx_int_t
+ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
+    ngx_quic_frame_t *start)
+{
+    ngx_queue_t       *q, range;
+    ngx_quic_frame_t  *f;
+
+    ngx_queue_init(&range);
+
+    /* send frames with same packet number to the wire */
+
+    q = ngx_queue_head(&ctx->sent);
+
+    do {
+        f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+        if (f->pnum != start->pnum) {
+            break;
+        }
+
+        q = ngx_queue_next(q);
+
+        ngx_queue_remove(&f->queue);
+        ngx_queue_insert_tail(&range, &f->queue);
+
+    } while (q != ngx_queue_sentinel(&ctx->sent));
+
+    ngx_quic_congestion_lost(c, start);
+
+    return ngx_quic_send_frames(c, ctx, &range);
+}
+
+
 ngx_connection_t *
 ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi)
 {