]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: support a max number of built packet per send iteration
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 11 Oct 2024 15:57:57 +0000 (17:57 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 31 Oct 2024 14:35:31 +0000 (15:35 +0100)
include/haproxy/quic_tx.h
src/quic_conn.c
src/quic_tx.c

index 830e6f89c62e7e7811cb8d6caa42d390ea3af36c..b77d6a20a5172ceff360a846ebf1efaf2e12a5b0 100644 (file)
@@ -38,7 +38,8 @@ int qc_send_mux(struct quic_conn *qc, struct list *frms);
 void qel_register_send(struct list *send_list, struct quic_enc_level *qel,
                        struct list *frms);
 int qel_need_sending(struct quic_enc_level *qel, struct quic_conn *qc);
-int qc_send(struct quic_conn *qc, int old_data, struct list *send_list);
+int qc_send(struct quic_conn *qc, int old_data, struct list *send_list,
+            int max_dgrams);
 
 int qc_dgrams_retransmit(struct quic_conn *qc);
 void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,
index 5c9577e639524d23eb80dda077fb6880a7d84917..afb9b26a212fe940f8ec31e30b1a6ab6ffd50c0a 100644 (file)
@@ -617,7 +617,7 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta
        if (qel_need_sending(qc->ael, qc))
                qel_register_send(&send_list, qc->ael, &qc->ael->pktns->tx.frms);
 
-       if (!qc_send(qc, 0, &send_list)) {
+       if (!qc_send(qc, 0, &send_list, 0)) {
                TRACE_DEVEL("qc_send() failed", QUIC_EV_CONN_IO_CB, qc);
                goto out;
        }
@@ -877,7 +877,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
                        qel_register_send(&send_list, qel, &qel->pktns->tx.frms);
        }
 
-       if (!qc_send(qc, 0, &send_list)) {
+       if (!qc_send(qc, 0, &send_list, 0)) {
                TRACE_DEVEL("qc_send() failed", QUIC_EV_CONN_IO_CB, qc);
                goto out;
        }
index 5a0d42aaf198c4d86b596c648d3f224aacbc1f6d..a4acaf344a6aaafc7a8da64b4d4c3a31736d9e61 100644 (file)
@@ -488,12 +488,12 @@ int qc_send_mux(struct quic_conn *qc, struct list *frms)
            qc->state >= QUIC_HS_ST_COMPLETE) {
                quic_build_post_handshake_frames(qc);
                qel_register_send(&send_list, qc->ael, &qc->ael->pktns->tx.frms);
-               qc_send(qc, 0, &send_list);
+               qc_send(qc, 0, &send_list, 0);
        }
 
        TRACE_STATE("preparing data (from MUX)", QUIC_EV_CONN_TXPKT, qc);
        qel_register_send(&send_list, qc->ael, frms);
-       ret = qc_send(qc, 0, &send_list);
+       ret = qc_send(qc, 0, &send_list, 0);
 
        TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
        return ret;
@@ -520,18 +520,21 @@ static inline void qc_select_tls_ver(struct quic_conn *qc,
        }
 }
 
-/* Prepare as much as possible QUIC datagrams/packets for sending from <qels>
- * list of encryption levels. Several packets can be coalesced into a single
+/* Prepare one or several QUIC datagrams/packets for sending from <qels> list
+ * of encryption levels. Several packets can be coalesced into a single
  * datagram. The result is written into <buf>.
  *
+ * If <max_dgrams> is non null, it limits the number of prepared datagrams.
+ * Useful to support pacing emission.
+ *
  * Each datagram is prepended by a two fields header : the datagram length and
  * the address of first packet in the datagram.
  *
- * Returns the number of bytes prepared in datragrams/packets if succeeded
- * (may be 0), or -1 if something wrong happened.
+ * Returns the number of prepared datagrams on success which may be 0. On error
+ * a negative error code is returned.
  */
 static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
-                         struct list *qels)
+                         struct list *qels, int max_dgrams)
 {
        int ret, cc, padding;
        struct quic_tx_packet *first_pkt, *prv_pkt;
@@ -540,7 +543,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
        uint16_t dglen;
        size_t total;
        struct quic_enc_level *qel, *tmp_qel;
-       uchar gso_dgram_cnt = 0;
+       uchar dgram_cnt = 0, gso_dgram_cnt = 0;
 
        TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
        /* Currently qc_prep_pkts() does not handle buffer wrapping so the
@@ -590,6 +593,15 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
 
                        TRACE_PROTO("TX prep pkts", QUIC_EV_CONN_PHPKTS, qc, qel);
 
+                       /* Start to decrement <max_dgrams> after the first packet built. */
+                       if (!dglen && pos != (unsigned char *)b_head(buf)) {
+                               if (max_dgrams && !--max_dgrams) {
+                                       BUG_ON(LIST_ISEMPTY(frms));
+                                       TRACE_PROTO("reached max allowed built datagrams", QUIC_EV_CONN_PHPKTS, qc, qel);
+                                       goto out;
+                               }
+                       }
+
                        if (!first_pkt)
                                pos += QUIC_DGRAM_HEADLEN;
 
@@ -647,8 +659,10 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                                         * except if it is an too short Initial.
                                         */
                                        if (first_pkt && (first_pkt->type != QUIC_PACKET_TYPE_INITIAL ||
-                                                         wrlen >= QUIC_INITIAL_PACKET_MINLEN))
+                                                         wrlen >= QUIC_INITIAL_PACKET_MINLEN)) {
                                                qc_txb_store(buf, wrlen, first_pkt);
+                                               ++dgram_cnt;
+                                       }
                                        TRACE_PROTO("could not prepare anymore packet", QUIC_EV_CONN_PHPKTS, qc, qel);
                                        break;
 
@@ -717,6 +731,8 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                                prv_pkt = cur_pkt;
                                dglen = 0;
 
+                               ++dgram_cnt;
+
                                /* man 7 udp UDP_SEGMENT
                                 * The segment size must be chosen such that at
                                 * most 64 datagrams are sent in a single call
@@ -730,6 +746,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                                wrlen = dglen = 0;
                                padding = 0;
                                prv_pkt = NULL;
+                               ++dgram_cnt;
                                gso_dgram_cnt = 0;
                        }
 
@@ -743,8 +760,10 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
        }
 
  out:
-       if (first_pkt)
+       if (first_pkt) {
                qc_txb_store(buf, wrlen, first_pkt);
+               ++dgram_cnt;
+       }
 
        if (cc && total) {
                BUG_ON(buf != &qc->tx.cc_buf);
@@ -752,7 +771,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                qc->tx.cc_dgram_len = dglen;
        }
 
-       ret = total;
+       ret = dgram_cnt;
  leave:
        TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
        return ret;
@@ -762,13 +781,21 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
  * specified via quic_enc_level <send_list> through their send_frms member. Set
  * <old_data> when reemitted duplicated data.
  *
-* Returns 1 on success else 0. Note that <send_list> will always be reset
-* after qc_send() exit.
+ * If <max_dgrams> is non null, it limits the number of emitted datagrams.
+ * Useful to support pacing emission.
+ *
+ * Note that <send_list> will always be emptied on function completion, both on
+ * success and error.
+ *
+ * Returns the number of sent datagrams on success. It means either that all
+ * input frames were sent or emission is interrupted due to pacing. Else a
+ * negative error code is returned.
  */
-int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
+int qc_send(struct quic_conn *qc, int old_data, struct list *send_list,
+            int max_dgrams)
 {
        struct quic_enc_level *qel, *tmp_qel;
-       int ret = 0, status = 0;
+       int prep_pkts = 0, ret = -1;
        struct buffer *buf;
 
        TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
@@ -796,25 +823,36 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
        while (!LIST_ISEMPTY(send_list) &&
               (!(qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_DRAINING)) ||
                (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))) {
+
+               if (ret < 0)
+                       ret = 0;
+
                /* Buffer must always be empty before qc_prep_pkts() usage.
                 * qc_send_ppkts() ensures it is cleared on success.
                 */
                BUG_ON_HOT(b_data(buf));
                b_reset(buf);
 
-               ret = qc_prep_pkts(qc, buf, send_list);
+               prep_pkts = qc_prep_pkts(qc, buf, send_list, max_dgrams);
 
                if (b_data(buf) && !qc_send_ppkts(buf, qc->xprt_ctx)) {
+                       ret = -1;
                        if (qc->flags & QUIC_FL_CONN_TO_KILL)
                                qc_txb_release(qc);
                        goto out;
                }
 
-               if (ret <= 0) {
+               if (prep_pkts <= 0) {
                        TRACE_DEVEL("stopping on qc_prep_pkts() return", QUIC_EV_CONN_TXPKT, qc);
                        break;
                }
 
+               ret += prep_pkts;
+               if (max_dgrams && ret == max_dgrams && !LIST_ISEMPTY(send_list)) {
+                       TRACE_DEVEL("stopping for artificial pacing", QUIC_EV_CONN_TXPKT, qc);
+                       break;
+               }
+
                if ((qc->flags & QUIC_FL_CONN_DRAINING) &&
                    !(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)) {
                        TRACE_DEVEL("draining connection", QUIC_EV_CONN_TXPKT, qc);
@@ -826,8 +864,6 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
        if (ret < 0)
                goto out;
 
-       status = 1;
-
  out:
        if (old_data) {
                TRACE_STATE("no more need old data for probing", QUIC_EV_CONN_TXPKT, qc);
@@ -840,8 +876,8 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
                qel->send_frms = NULL;
        }
 
-       TRACE_DEVEL((status ? "leaving" : "leaving in error"), QUIC_EV_CONN_TXPKT, qc);
-       return status;
+       TRACE_DEVEL((ret > 0 ? "leaving" : "leaving in error"), QUIC_EV_CONN_TXPKT, qc);
+       return ret;
 }
 
 /* Insert <qel> into <send_list> in preparation for sending. Set its send
@@ -905,10 +941,10 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
                                if (qc->hel)
                                        qel_register_send(&send_list, qc->hel, &hfrms);
 
-                               sret = qc_send(qc, 1, &send_list);
+                               sret = qc_send(qc, 1, &send_list, 0);
                                qc_free_frm_list(qc, &ifrms);
                                qc_free_frm_list(qc, &hfrms);
-                               if (!sret)
+                               if (sret < 0)
                                        goto leave;
                        }
                        else {
@@ -918,10 +954,10 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
                                 */
                                ipktns->tx.pto_probe = 1;
                                qel_register_send(&send_list, qc->iel, &ifrms);
-                               sret = qc_send(qc, 0, &send_list);
+                               sret = qc_send(qc, 0, &send_list, 0);
                                qc_free_frm_list(qc, &ifrms);
                                qc_free_frm_list(qc, &hfrms);
-                               if (!sret)
+                               if (sret < 0)
                                        goto leave;
 
                                break;
@@ -947,9 +983,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
                                if (!LIST_ISEMPTY(&frms1)) {
                                        hpktns->tx.pto_probe = 1;
                                        qel_register_send(&send_list, qc->hel, &frms1);
-                                       sret = qc_send(qc, 1, &send_list);
+                                       sret = qc_send(qc, 1, &send_list, 0);
                                        qc_free_frm_list(qc, &frms1);
-                                       if (!sret)
+                                       if (sret < 0)
                                                goto leave;
                                }
                        }
@@ -970,9 +1006,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
                        if (!LIST_ISEMPTY(&frms1)) {
                                apktns->tx.pto_probe = 1;
                                qel_register_send(&send_list, qc->ael, &frms1);
-                               sret = qc_send(qc, 1, &send_list);
+                               sret = qc_send(qc, 1, &send_list, 0);
                                qc_free_frm_list(qc, &frms1);
-                               if (!sret) {
+                               if (sret < 0) {
                                        qc_free_frm_list(qc, &frms2);
                                        goto leave;
                                }
@@ -981,9 +1017,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
                        if (!LIST_ISEMPTY(&frms2)) {
                                apktns->tx.pto_probe = 1;
                                qel_register_send(&send_list, qc->ael, &frms2);
-                               sret = qc_send(qc, 1, &send_list);
+                               sret = qc_send(qc, 1, &send_list, 0);
                                qc_free_frm_list(qc, &frms2);
-                               if (!sret)
+                               if (sret < 0)
                                        goto leave;
                        }
                        TRACE_STATE("no more need to probe 01RTT packet number space",