From: Amaury Denoyelle Date: Fri, 11 Oct 2024 15:57:57 +0000 (+0200) Subject: MINOR: quic: support a max number of built packet per send iteration X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4618b48c19c41959381a89de266aaf01a5ed9dd2;p=thirdparty%2Fhaproxy.git MINOR: quic: support a max number of built packet per send iteration --- diff --git a/include/haproxy/quic_tx.h b/include/haproxy/quic_tx.h index 830e6f89c6..b77d6a20a5 100644 --- a/include/haproxy/quic_tx.h +++ b/include/haproxy/quic_tx.h @@ -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, diff --git a/src/quic_conn.c b/src/quic_conn.c index 5c9577e639..afb9b26a21 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -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; } diff --git a/src/quic_tx.c b/src/quic_tx.c index 5a0d42aaf1..a4acaf344a 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -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 - * list of encryption levels. Several packets can be coalesced into a single +/* Prepare one or several QUIC datagrams/packets for sending from list + * of encryption levels. Several packets can be coalesced into a single * datagram. The result is written into . * + * If 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 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 through their send_frms member. Set * when reemitted duplicated data. * -* Returns 1 on success else 0. Note that will always be reset -* after qc_send() exit. + * If is non null, it limits the number of emitted datagrams. + * Useful to support pacing emission. + * + * Note that 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 into 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",