]> git.kaiwu.me - nginx.git/commitdiff
QUIC: avoid excessive buffer allocations in stream output.
authorRoman Arutyunyan <arut@nginx.com>
Fri, 24 Dec 2021 15:13:51 +0000 (18:13 +0300)
committerRoman Arutyunyan <arut@nginx.com>
Fri, 24 Dec 2021 15:13:51 +0000 (18:13 +0300)
Previously, when a few bytes were send to a QUIC stream by the application, a
4K buffer was allocated for these bytes.  Then a STREAM frame was created and
that entire buffer was used as data for that frame.  The frame with the buffer
were in use up until the frame was acked by client.  Meanwhile, when more
bytes were send to the stream, more buffers were allocated and assigned as
data to newer STREAM frames.  In this scenario most buffer memory is unused.

Now the unused part of the stream output buffer is available for further
stream output while earlier parts of the buffer are waiting to be acked.
This is achieved by splitting the output buffer.

src/event/quic/ngx_event_quic.h
src/event/quic/ngx_event_quic_frames.c
src/event/quic/ngx_event_quic_frames.h
src/event/quic/ngx_event_quic_ssl.c
src/event/quic/ngx_event_quic_streams.c

index 8ae7bf6436fea0031762550c2efa7bdf9fd74d84..9481fef62858f827f8191091b14f8ddf39461fd5 100644 (file)
@@ -63,6 +63,7 @@ struct ngx_quic_stream_s {
     uint64_t                   recv_last;
     uint64_t                   final_size;
     ngx_chain_t               *in;
+    ngx_chain_t               *out;
     ngx_uint_t                 cancelable;  /* unsigned  cancelable:1; */
 };
 
index 4a3902f7f72b676cd9fc15e5b407aeb99b5693d9..71ed981e6afb8f4de2978946005c1f3f16ae5a66 100644 (file)
@@ -482,14 +482,14 @@ done:
 
 ngx_int_t
 ngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out, ngx_chain_t *in,
-    size_t offset)
+    off_t limit, off_t offset)
 {
+    off_t         n;
     u_char       *p;
-    size_t        n;
     ngx_buf_t    *b;
     ngx_chain_t  *cl, *sl;
 
-    while (in) {
+    while (in && limit) {
         cl = *out;
 
         if (cl == NULL) {
@@ -523,8 +523,9 @@ ngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out, ngx_chain_t *in,
             continue;
         }
 
-        for (p = b->pos + offset; p != b->last && in; /* void */ ) {
+        for (p = b->pos + offset; p != b->last && in && limit; /* void */ ) {
             n = ngx_min(b->last - p, in->buf->last - in->buf->pos);
+            n = ngx_min(n, limit);
 
             if (b->sync) {
                 ngx_memcpy(p, in->buf->pos, n);
@@ -533,6 +534,7 @@ ngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out, ngx_chain_t *in,
             p += n;
             in->buf->pos += n;
             offset += n;
+            limit -= n;
 
             if (in->buf->pos == in->buf->last) {
                 in = in->next;
index f4c1476825864a2dcd28f909569af55ba3fefc2c..1310f6dfdf542b1f3c412439354f9761d2d0f238 100644 (file)
@@ -31,7 +31,7 @@ ngx_chain_t *ngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in,
 void ngx_quic_trim_bufs(ngx_chain_t *in, size_t size);
 void ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in);
 ngx_int_t ngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out,
-    ngx_chain_t *in, size_t offset);
+    ngx_chain_t *in, off_t limit, off_t offset);
 
 #if (NGX_DEBUG)
 void ngx_quic_log_frame(ngx_log_t *log, ngx_quic_frame_t *f, ngx_uint_t tx);
index fb4b1af85fc3fb03c4b3d772aeb90942fcdee318..d2f1237a573aba4fa9d07cd1705654d720dd46f6 100644 (file)
@@ -369,7 +369,7 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
     }
 
     if (f->offset > ctx->crypto_received) {
-        return ngx_quic_order_bufs(c, &ctx->crypto, frame->data,
+        return ngx_quic_order_bufs(c, &ctx->crypto, frame->data, f->length,
                                    f->offset - ctx->crypto_received);
     }
 
index 295aa54aa70d675099b34c1d2829dbb99650020a..d5facc27020dee649c6d912fc65cb6a86e145b27 100644 (file)
@@ -838,8 +838,9 @@ static ngx_chain_t *
 ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 {
     size_t                  n, flow;
+    ngx_buf_t              *b;
     ngx_event_t            *wev;
-    ngx_chain_t            *cl;
+    ngx_chain_t            *out, **ll;
     ngx_connection_t       *pc;
     ngx_quic_frame_t       *frame;
     ngx_quic_stream_t      *qs;
@@ -862,18 +863,30 @@ ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 
     n = (limit && (size_t) limit < flow) ? (size_t) limit : flow;
 
-    frame = ngx_quic_alloc_frame(pc);
-    if (frame == NULL) {
+    if (ngx_quic_order_bufs(pc, &qs->out, in, n, 0) != NGX_OK) {
         return NGX_CHAIN_ERROR;
     }
 
-    frame->data = ngx_quic_copy_chain(pc, in, n);
-    if (frame->data == NGX_CHAIN_ERROR) {
-        return NGX_CHAIN_ERROR;
+    n = 0;
+    out = qs->out;
+
+    for (ll = &out; *ll; ll = &(*ll)->next) {
+        b = (*ll)->buf;
+
+        if (b->sync) {
+            /* hole */
+            break;
+        }
+
+        n += b->last - b->pos;
     }
 
-    for (n = 0, cl = frame->data; cl; cl = cl->next) {
-        n += ngx_buf_size(cl->buf);
+    qs->out = *ll;
+    *ll = NULL;
+
+    frame = ngx_quic_alloc_frame(pc);
+    if (frame == NULL) {
+        return NGX_CHAIN_ERROR;
     }
 
     while (in && ngx_buf_size(in->buf) == 0) {
@@ -882,6 +895,7 @@ ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 
     frame->level = ssl_encryption_application;
     frame->type = NGX_QUIC_FT_STREAM;
+    frame->data = out;
     frame->u.stream.off = 1;
     frame->u.stream.len = 1;
     frame->u.stream.fin = 0;
@@ -977,6 +991,7 @@ ngx_quic_stream_cleanup_handler(void *data)
 
     ngx_rbtree_delete(&qc->streams.tree, &qs->node);
     ngx_quic_free_bufs(pc, qs->in);
+    ngx_quic_free_bufs(pc, qs->out);
 
     if (qc->closing) {
         /* schedule handler call to continue ngx_quic_close_connection() */
@@ -1098,7 +1113,7 @@ ngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
         qs->final_size = last;
     }
 
-    if (ngx_quic_order_bufs(c, &qs->in, frame->data,
+    if (ngx_quic_order_bufs(c, &qs->in, frame->data, f->length,
                             f->offset - qs->recv_offset)
         != NGX_OK)
     {