]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: h3: Implement zero-copy support to send DATA frame
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 27 Oct 2023 14:41:58 +0000 (16:41 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 24 Nov 2023 06:42:43 +0000 (07:42 +0100)
When possible, we try send DATA frame without copying data. To do so, we
swap the input buffer with QCS tx buffer. It is only possible iff:

 * There is only one HTX block of data at the beginning of the message
 * Amount of data to send is equal to the size of the HTX data block
 * The QCS tx buffer is empty

In this case, both buffers are swapped. The frame metadata are written at
the begining of the buffer, before data and where the HTX structure is
stored.

include/haproxy/mux_quic-t.h
src/h3.c
src/hq_interop.c
src/qmux_http.c

index f293d96d51e15ae2870dfc9f37e1628f5df986e4..abfc20a506ce611a31045aab965fa72c19c70524 100644 (file)
@@ -188,7 +188,7 @@ struct qcc_app_ops {
        int (*init)(struct qcc *qcc);
        int (*attach)(struct qcs *qcs, void *conn_ctx);
        ssize_t (*decode_qcs)(struct qcs *qcs, struct buffer *b, int fin);
-       size_t (*snd_buf)(struct qcs *qcs, struct htx *htx, size_t count);
+       size_t (*snd_buf)(struct qcs *qcs, struct buffer *buf, size_t count);
        size_t (*nego_ff)(struct qcs *qcs, size_t count);
        size_t (*done_ff)(struct qcs *qcs);
        int (*close)(struct qcs *qcs, enum qcc_app_ops_close_side side);
index 6ae6ee2119d74f735f3de02b140c2342d11cd365..793388df0f8455270a8c16b014fc2d446006315d 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -1674,8 +1674,9 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
 }
 
 /* Returns the total of bytes sent. */
-static int h3_resp_data_send(struct qcs *qcs, struct htx *htx, size_t count)
+static int h3_resp_data_send(struct qcs *qcs, struct buffer *buf, size_t count)
 {
+       struct htx *htx;
        struct buffer outbuf;
        struct buffer *res;
        size_t total = 0;
@@ -1685,6 +1686,8 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx, size_t count)
 
        TRACE_ENTER(H3_EV_TX_DATA, qcs->qcc->conn, qcs);
 
+       htx = htx_from_buf(buf);
+
  new_frame:
        if (!count || htx_is_empty(htx))
                goto end;
@@ -1693,17 +1696,38 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx, size_t count)
        type = htx_get_blk_type(blk);
        fsize = bsize = htx_get_blksz(blk);
 
+       /* h3 DATA headers : 1-byte frame type + varint frame length */
+       hsize = 1 + QUIC_VARINT_MAX_SIZE;
+
        if (type != HTX_BLK_DATA)
                goto end;
 
        res = mux_get_buf(qcs);
 
+       if (unlikely(fsize == count &&
+                    !b_data(res) &&
+                    htx_nbblks(htx) == 1 && type == HTX_BLK_DATA)) {
+               void *old_area = res->area;
+
+               /* map an H2 frame to the HTX block so that we can put the
+                * frame header there.
+                */
+               *res = b_make(buf->area, buf->size, sizeof(struct htx) + blk->addr - hsize, fsize + hsize);
+               outbuf = b_make(b_head(res), hsize, 0, 0);
+               b_putchr(&outbuf, 0x00); /* h3 frame type = DATA */
+               b_quic_enc_int(&outbuf, fsize, QUIC_VARINT_MAX_SIZE); /* h3 frame length */
+
+               /* and exchange with our old area */
+               buf->area = old_area;
+               buf->data = buf->head = 0;
+               total += fsize;
+               fsize = 0;
+               goto end;
+       }
+
        if (fsize > count)
                fsize = count;
 
-       /* h3 DATA headers : 1-byte frame type + varint frame length */
-       hsize = 1 + QUIC_VARINT_MAX_SIZE;
-
        while (1) {
                b_reset(&outbuf);
                outbuf = b_make(b_tail(res), b_contig_space(res), 0, 0);
@@ -1746,10 +1770,11 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx, size_t count)
        return total;
 }
 
-static size_t h3_snd_buf(struct qcs *qcs, struct htx *htx, size_t count)
+static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count)
 {
        size_t total = 0;
        enum htx_blk_type btype;
+       struct htx *htx;
        struct htx_blk *blk;
        uint32_t bsize;
        int32_t idx;
@@ -1757,6 +1782,11 @@ static size_t h3_snd_buf(struct qcs *qcs, struct htx *htx, size_t count)
 
        h3_debug_printf(stderr, "%s\n", __func__);
 
+       htx = htx_from_buf(buf);
+
+       if (htx->extra && htx->extra == HTX_UNKOWN_PAYLOAD_LENGTH)
+               qcs->flags |= QC_SF_UNKNOWN_PL_LENGTH;
+
        while (count && !htx_is_empty(htx) && !(qcs->flags & QC_SF_BLK_MROOM)) {
                idx = htx_get_head(htx);
                blk = htx_get_blk(htx, idx);
@@ -1779,8 +1809,9 @@ static size_t h3_snd_buf(struct qcs *qcs, struct htx *htx, size_t count)
                        break;
 
                case HTX_BLK_DATA:
-                       ret = h3_resp_data_send(qcs, htx, count);
+                       ret = h3_resp_data_send(qcs, buf, count);
                        if (ret > 0) {
+                               htx = htx_from_buf(buf);
                                total += ret;
                                count -= ret;
                                if (ret < bsize)
@@ -1833,6 +1864,8 @@ static size_t h3_snd_buf(struct qcs *qcs, struct htx *htx, size_t count)
        }
 
  out:
+       htx_to_buf(htx, buf);
+
        return total;
 }
 
index 8126a9390521aa4ee485cc622d669ab27a81600e..31c2101ec7252dc7ce9fcc43c45520a83cde3da1 100644 (file)
@@ -91,10 +91,11 @@ static struct buffer *mux_get_buf(struct qcs *qcs)
        return &qcs->tx.buf;
 }
 
-static size_t hq_interop_snd_buf(struct qcs *qcs, struct htx *htx,
+static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
                                  size_t count)
 {
        enum htx_blk_type btype;
+       struct htx *htx;
        struct htx_blk *blk;
        int32_t idx;
        uint32_t bsize, fsize;
@@ -104,6 +105,11 @@ static size_t hq_interop_snd_buf(struct qcs *qcs, struct htx *htx,
        res = mux_get_buf(qcs);
        outbuf = b_make(b_tail(res), b_contig_space(res), 0, 0);
 
+       htx = htx_from_buf(buf);
+
+       if (htx->extra && htx->extra == HTX_UNKOWN_PAYLOAD_LENGTH)
+               qcs->flags |= QC_SF_UNKNOWN_PL_LENGTH;
+
        while (count && !htx_is_empty(htx) && !(qcs->flags & QC_SF_BLK_MROOM)) {
                /* Not implemented : QUIC on backend side */
                idx = htx_get_head(htx);
@@ -150,6 +156,7 @@ static size_t hq_interop_snd_buf(struct qcs *qcs, struct htx *htx,
 
  end:
        b_add(res, b_data(&outbuf));
+       htx_to_buf(htx, buf);
 
        return total;
 }
index fc891dc53ade050699a4a6c3006dc507292c96ae..edf26b1418f1592b14c905b2db33148773950744 100644 (file)
@@ -73,18 +73,14 @@ size_t qcs_http_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count,
 {
        struct htx *htx;
        size_t ret;
+       int eom = 0;
 
        TRACE_ENTER(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
 
-       htx = htx_from_buf(buf);
-
-       if (htx->extra && htx->extra == HTX_UNKOWN_PAYLOAD_LENGTH)
-               qcs->flags |= QC_SF_UNKNOWN_PL_LENGTH;
-
-       ret = qcs->qcc->app_ops->snd_buf(qcs, htx, count);
-       *fin = (htx->flags & HTX_FL_EOM) && htx_is_empty(htx);
-
-       htx_to_buf(htx, buf);
+       htx = htxbuf(buf);
+       eom = (htx->flags & HTX_FL_EOM);
+       ret = qcs->qcc->app_ops->snd_buf(qcs, buf, count);
+       *fin = (eom && !b_data(buf));
 
        TRACE_LEAVE(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);