changeset 8697:faa3201ff351 quic

QUIC: improved setting the lost timer. Setting the timer is brought into compliance with quic-recovery-34. Now it's set from a single function ngx_quic_set_lost_timer() that takes into account both loss detection and PTO. The following issues are fixed with this change: - when in loss detection mode, discarding a context could turn off the timer forever after switching to the PTO mode - when in loss detection mode, sending a packet resulted in rescheduling the timer as if it's always in the PTO mode
author Roman Arutyunyan <arut@nginx.com>
date Fri, 12 Feb 2021 14:40:33 +0300
parents 88c9c868a7c9
children 9ed95726b99b
files src/event/quic/ngx_event_quic.c
diffstat 1 files changed, 88 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -24,6 +24,10 @@
         : (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1])       \
                                                  : &((qc)->send_ctx[2]))
 
+#define ngx_quic_lost_threshold(qc)                                           \
+    ngx_max(NGX_QUIC_TIME_THR * ngx_max((qc)->latest_rtt, (qc)->avg_rtt),     \
+            NGX_QUIC_TIME_GRANULARITY)
+
 #define NGX_QUIC_SEND_CTX_LAST  (NGX_QUIC_ENCRYPTION_LAST - 1)
 
 /*
@@ -357,6 +361,7 @@ static void ngx_quic_set_packet_number(n
 static void ngx_quic_pto_handler(ngx_event_t *ev);
 static void ngx_quic_lost_handler(ngx_event_t *ev);
 static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c);
+static void ngx_quic_set_lost_timer(ngx_connection_t *c);
 static void ngx_quic_resend_frames(ngx_connection_t *c,
     ngx_quic_send_ctx_t *ctx);
 static void ngx_quic_push_handler(ngx_event_t *ev);
@@ -2607,6 +2612,8 @@ ngx_quic_discard_ctx(ngx_connection_t *c
     }
 
     ctx->send_ack = 0;
+
+    ngx_quic_set_lost_timer(c);
 }
 
 
@@ -4920,6 +4927,8 @@ ngx_quic_output(ngx_connection_t *c)
         }
     }
 
+    ngx_quic_set_lost_timer(c);
+
     return NGX_OK;
 }
 
@@ -5167,12 +5176,6 @@ ngx_quic_output_packet(ngx_connection_t 
                 ngx_queue_remove(q);
                 ngx_queue_insert_tail(&ctx->sent, q);
             } while (--nframes);
-
-            if (qc->pto.timer_set) {
-                ngx_del_timer(&qc->pto);
-            }
-
-            ngx_add_timer(&qc->pto, ngx_quic_pto(c, ctx));
         }
 
         cg->in_flight += res.len;
@@ -5423,7 +5426,7 @@ static ngx_int_t
 ngx_quic_detect_lost(ngx_connection_t *c)
 {
     ngx_uint_t              i;
-    ngx_msec_t              now, wait, min_wait, thr;
+    ngx_msec_t              now, wait, thr;
     ngx_queue_t            *q;
     ngx_quic_frame_t       *start;
     ngx_quic_send_ctx_t    *ctx;
@@ -5431,11 +5434,7 @@ ngx_quic_detect_lost(ngx_connection_t *c
 
     qc = ngx_quic_get_connection(c);
     now = ngx_current_msec;
-
-    min_wait = 0;
-
-    thr = NGX_QUIC_TIME_THR * ngx_max(qc->latest_rtt, qc->avg_rtt);
-    thr = ngx_max(thr, NGX_QUIC_TIME_GRANULARITY);
+    thr = ngx_quic_lost_threshold(qc);
 
     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
 
@@ -5463,11 +5462,6 @@ ngx_quic_detect_lost(ngx_connection_t *c
             if ((ngx_msec_int_t) wait > 0
                 && ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
             {
-
-                if (min_wait == 0 || wait < min_wait) {
-                    min_wait = wait;
-                }
-
                 break;
             }
 
@@ -5475,22 +5469,88 @@ ngx_quic_detect_lost(ngx_connection_t *c
         }
     }
 
-    /* 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;
+    ngx_quic_set_lost_timer(c);
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_quic_set_lost_timer(ngx_connection_t *c)
+{
+    ngx_uint_t              i;
+    ngx_msec_t              now;
+    ngx_queue_t            *q;
+    ngx_msec_int_t          lost, pto, w;
+    ngx_quic_frame_t       *f;
+    ngx_quic_send_ctx_t    *ctx;
+    ngx_quic_connection_t  *qc;
+
+    qc = ngx_quic_get_connection(c);
+    now = ngx_current_msec;
+
+    lost = -1;
+    pto = -1;
+
+    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
+        ctx = &qc->send_ctx[i];
+
+        if (ngx_queue_empty(&ctx->sent)) {
+            continue;
+        }
+
+        if (ctx->largest_ack != NGX_QUIC_UNSET_PN) {
+            q = ngx_queue_head(&ctx->sent);
+            f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+            w = (ngx_msec_int_t) (f->last + ngx_quic_lost_threshold(qc) - now);
+
+            if (f->pnum <= ctx->largest_ack) {
+                if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
+                    w = 0;
+                }
+
+                if (lost == -1 || w < lost) {
+                    lost = w;
+                }
+            }
+        }
+
+        q = ngx_queue_last(&ctx->sent);
+        f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+        w = (ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now);
+
+        if (w < 0) {
+            w = 0;
+        }
+
+        if (pto == -1 || w < pto) {
+            pto = w;
+        }
+    }
 
     if (qc->pto.timer_set) {
         ngx_del_timer(&qc->pto);
     }
 
-    ngx_add_timer(&qc->pto, min_wait);
-
-    return NGX_OK;
+    if (lost != -1) {
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic lost timer lost:%M", lost);
+
+        qc->pto.handler = ngx_quic_lost_handler;
+        ngx_add_timer(&qc->pto, lost);
+        return;
+    }
+
+    if (pto != -1) {
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic lost timer pto:%M", pto);
+
+        qc->pto.handler = ngx_quic_pto_handler;
+        ngx_add_timer(&qc->pto, pto);
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic lost timer unset");
 }