Skip to content

Commit 5cb8f8a

Browse files
committed
MINOR: quic: support a max number of built packet per send iteration
Extend QUIC transport emission function to support a maximum datagram argument. The purpose is to ensure that qc_send() won't emit more than the specified value, unless it is 0 which is considered as unlimited. In qc_prep_pkts(), a counter of built datagram has been added to support this. The packet building loop is interrupted if it reaches a specified maximum value. Also, its return value has been changed to the number of prepared datagrams. This is reused by qc_send() to interrupt its work if a specified max datagram argument value is reached over one or several iteration of prepared/sent datagrams. This change is necessary to support pacing emission. Note that ideally, the total length in bytes of emitted datagrams should be taken into account instead of the raw number of datagrams. However, for a first implementation, it was deemed easier to implement it with the latter.
1 parent a554d82 commit 5cb8f8a

File tree

3 files changed

+70
-34
lines changed

3 files changed

+70
-34
lines changed

include/haproxy/quic_tx.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ int qc_send_mux(struct quic_conn *qc, struct list *frms);
3838
void qel_register_send(struct list *send_list, struct quic_enc_level *qel,
3939
struct list *frms);
4040
int qel_need_sending(struct quic_enc_level *qel, struct quic_conn *qc);
41-
int qc_send(struct quic_conn *qc, int old_data, struct list *send_list);
41+
int qc_send(struct quic_conn *qc, int old_data, struct list *send_list,
42+
int max_dgrams);
4243

4344
int qc_dgrams_retransmit(struct quic_conn *qc);
4445
void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,

src/quic_conn.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta
617617
if (qel_need_sending(qc->ael, qc))
618618
qel_register_send(&send_list, qc->ael, &qc->ael->pktns->tx.frms);
619619

620-
if (!qc_send(qc, 0, &send_list)) {
620+
if (!qc_send(qc, 0, &send_list, 0)) {
621621
TRACE_DEVEL("qc_send() failed", QUIC_EV_CONN_IO_CB, qc);
622622
goto out;
623623
}
@@ -877,7 +877,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
877877
qel_register_send(&send_list, qel, &qel->pktns->tx.frms);
878878
}
879879

880-
if (!qc_send(qc, 0, &send_list)) {
880+
if (!qc_send(qc, 0, &send_list, 0)) {
881881
TRACE_DEVEL("qc_send() failed", QUIC_EV_CONN_IO_CB, qc);
882882
goto out;
883883
}

src/quic_tx.c

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -488,12 +488,12 @@ int qc_send_mux(struct quic_conn *qc, struct list *frms)
488488
qc->state >= QUIC_HS_ST_COMPLETE) {
489489
quic_build_post_handshake_frames(qc);
490490
qel_register_send(&send_list, qc->ael, &qc->ael->pktns->tx.frms);
491-
qc_send(qc, 0, &send_list);
491+
qc_send(qc, 0, &send_list, 0);
492492
}
493493

494494
TRACE_STATE("preparing data (from MUX)", QUIC_EV_CONN_TXPKT, qc);
495495
qel_register_send(&send_list, qc->ael, frms);
496-
ret = qc_send(qc, 0, &send_list);
496+
ret = qc_send(qc, 0, &send_list, 0);
497497

498498
TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
499499
return ret;
@@ -520,18 +520,21 @@ static inline void qc_select_tls_ver(struct quic_conn *qc,
520520
}
521521
}
522522

523-
/* Prepare as much as possible QUIC datagrams/packets for sending from <qels>
524-
* list of encryption levels. Several packets can be coalesced into a single
523+
/* Prepare one or several QUIC datagrams/packets for sending from <qels> list
524+
* of encryption levels. Several packets can be coalesced into a single
525525
* datagram. The result is written into <buf>.
526526
*
527+
* If <max_dgrams> is non null, it limits the number of prepared datagrams.
528+
* Useful to support pacing emission.
529+
*
527530
* Each datagram is prepended by a two fields header : the datagram length and
528531
* the address of first packet in the datagram.
529532
*
530-
* Returns the number of bytes prepared in datragrams/packets if succeeded
531-
* (may be 0), or -1 if something wrong happened.
533+
* Returns the number of prepared datagrams on success which may be 0. On error
534+
* a negative error code is returned.
532535
*/
533536
static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
534-
struct list *qels)
537+
struct list *qels, int max_dgrams)
535538
{
536539
int cc, padding;
537540
struct quic_tx_packet *first_pkt, *prv_pkt;
@@ -540,6 +543,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
540543
uint16_t dglen;
541544
int total = 0;
542545
struct quic_enc_level *qel, *tmp_qel;
546+
int dgram_cnt = 0;
543547
uchar gso_dgram_cnt = 0;
544548

545549
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
@@ -588,6 +592,15 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
588592

589593
TRACE_PROTO("TX prep pkts", QUIC_EV_CONN_PHPKTS, qc, qel);
590594

595+
/* Start to decrement <max_dgrams> after the first packet built. */
596+
if (!dglen && pos != (unsigned char *)b_head(buf)) {
597+
if (max_dgrams && !--max_dgrams) {
598+
BUG_ON(LIST_ISEMPTY(frms));
599+
TRACE_PROTO("reached max allowed built datagrams", QUIC_EV_CONN_PHPKTS, qc, qel);
600+
goto out;
601+
}
602+
}
603+
591604
if (!first_pkt)
592605
pos += QUIC_DGRAM_HEADLEN;
593606

@@ -655,8 +668,10 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
655668
* except if it is an too short Initial.
656669
*/
657670
if (first_pkt && (first_pkt->type != QUIC_PACKET_TYPE_INITIAL ||
658-
wrlen >= QUIC_INITIAL_PACKET_MINLEN))
671+
wrlen >= QUIC_INITIAL_PACKET_MINLEN)) {
659672
qc_txb_store(buf, wrlen, first_pkt);
673+
++dgram_cnt;
674+
}
660675
TRACE_PROTO("could not prepare anymore packet", QUIC_EV_CONN_PHPKTS, qc, qel);
661676
break;
662677

@@ -725,6 +740,8 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
725740
prv_pkt = cur_pkt;
726741
dglen = 0;
727742

743+
++dgram_cnt;
744+
728745
/* man 7 udp UDP_SEGMENT
729746
* The segment size must be chosen such that at
730747
* most 64 datagrams are sent in a single call
@@ -738,6 +755,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
738755
wrlen = dglen = 0;
739756
padding = 0;
740757
prv_pkt = NULL;
758+
++dgram_cnt;
741759
gso_dgram_cnt = 0;
742760
}
743761

@@ -761,7 +779,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
761779
}
762780

763781
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
764-
return total;
782+
return dgram_cnt;
765783

766784
err:
767785
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_PHPKTS, qc);
@@ -772,25 +790,35 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
772790
* specified via quic_enc_level <send_list> through their send_frms member. Set
773791
* <old_data> when reemitted duplicated data.
774792
*
775-
* Returns 1 on success else 0. Note that <send_list> will always be reset
776-
* after qc_send() exit.
793+
* If <max_dgrams> is non null, it limits the number of emitted datagrams.
794+
* Useful to support pacing emission.
795+
*
796+
* Note that <send_list> will always be emptied on function completion, both on
797+
* success and error.
798+
*
799+
* Returns the number of sent datagrams on success. It means either that all
800+
* input frames were sent or emission is interrupted due to pacing. Else a
801+
* negative error code is returned.
777802
*/
778-
int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
803+
int qc_send(struct quic_conn *qc, int old_data, struct list *send_list,
804+
int max_dgrams)
779805
{
780806
struct quic_enc_level *qel, *tmp_qel;
781-
int ret = 0, status = 0;
807+
int prep = 0, ret = 0;
782808
struct buffer *buf;
783809

784810
TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
785811

786812
buf = qc_get_txb(qc);
787813
if (!buf) {
788814
TRACE_ERROR("buffer allocation failed", QUIC_EV_CONN_TXPKT, qc);
815+
ret = -1;
789816
goto out;
790817
}
791818

792819
if (b_data(buf) && !qc_purge_txbuf(qc, buf)) {
793820
TRACE_ERROR("Could not purge TX buffer", QUIC_EV_CONN_TXPKT, qc);
821+
ret = -1;
794822
goto out;
795823
}
796824

@@ -806,25 +834,36 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
806834
while (!LIST_ISEMPTY(send_list) &&
807835
(!(qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_DRAINING)) ||
808836
(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))) {
837+
809838
/* Buffer must always be empty before qc_prep_pkts() usage.
810839
* qc_send_ppkts() ensures it is cleared on success.
811840
*/
812841
BUG_ON_HOT(b_data(buf));
813842
b_reset(buf);
814843

815-
ret = qc_prep_pkts(qc, buf, send_list);
844+
prep = qc_prep_pkts(qc, buf, send_list, max_dgrams ? max_dgrams - ret : 0);
845+
BUG_ON(max_dgrams && prep > max_dgrams);
816846

817847
if (b_data(buf) && !qc_send_ppkts(buf, qc->xprt_ctx)) {
818848
if (qc->flags & QUIC_FL_CONN_TO_KILL)
819849
qc_txb_release(qc);
850+
ret = -1;
820851
goto out;
821852
}
822853

823-
if (ret <= 0) {
854+
if (prep <= 0) {
855+
/* TODO should this be considered error if prep<0 ? */
824856
TRACE_DEVEL("stopping on qc_prep_pkts() return", QUIC_EV_CONN_TXPKT, qc);
825857
break;
826858
}
827859

860+
ret += prep;
861+
BUG_ON(max_dgrams && ret > max_dgrams);
862+
if (max_dgrams && ret == max_dgrams && !LIST_ISEMPTY(send_list)) {
863+
TRACE_DEVEL("stopping for artificial pacing", QUIC_EV_CONN_TXPKT, qc);
864+
break;
865+
}
866+
828867
if ((qc->flags & QUIC_FL_CONN_DRAINING) &&
829868
!(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)) {
830869
TRACE_DEVEL("draining connection", QUIC_EV_CONN_TXPKT, qc);
@@ -833,10 +872,6 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
833872
}
834873

835874
qc_txb_release(qc);
836-
if (ret < 0)
837-
goto out;
838-
839-
status = 1;
840875

841876
out:
842877
if (old_data) {
@@ -850,8 +885,8 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
850885
qel->send_frms = NULL;
851886
}
852887

853-
TRACE_DEVEL((status ? "leaving" : "leaving in error"), QUIC_EV_CONN_TXPKT, qc);
854-
return status;
888+
TRACE_DEVEL((ret > 0 ? "leaving" : "leaving in error"), QUIC_EV_CONN_TXPKT, qc);
889+
return ret;
855890
}
856891

857892
/* Insert <qel> into <send_list> in preparation for sending. Set its send
@@ -915,10 +950,10 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
915950
if (qc->hel)
916951
qel_register_send(&send_list, qc->hel, &hfrms);
917952

918-
sret = qc_send(qc, 1, &send_list);
953+
sret = qc_send(qc, 1, &send_list, 0);
919954
qc_free_frm_list(qc, &ifrms);
920955
qc_free_frm_list(qc, &hfrms);
921-
if (!sret)
956+
if (sret < 0)
922957
goto leave;
923958
}
924959
else {
@@ -928,10 +963,10 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
928963
*/
929964
ipktns->tx.pto_probe = 1;
930965
qel_register_send(&send_list, qc->iel, &ifrms);
931-
sret = qc_send(qc, 0, &send_list);
966+
sret = qc_send(qc, 0, &send_list, 0);
932967
qc_free_frm_list(qc, &ifrms);
933968
qc_free_frm_list(qc, &hfrms);
934-
if (!sret)
969+
if (sret < 0)
935970
goto leave;
936971

937972
break;
@@ -957,9 +992,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
957992
if (!LIST_ISEMPTY(&frms1)) {
958993
hpktns->tx.pto_probe = 1;
959994
qel_register_send(&send_list, qc->hel, &frms1);
960-
sret = qc_send(qc, 1, &send_list);
995+
sret = qc_send(qc, 1, &send_list, 0);
961996
qc_free_frm_list(qc, &frms1);
962-
if (!sret)
997+
if (sret < 0)
963998
goto leave;
964999
}
9651000
}
@@ -980,9 +1015,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
9801015
if (!LIST_ISEMPTY(&frms1)) {
9811016
apktns->tx.pto_probe = 1;
9821017
qel_register_send(&send_list, qc->ael, &frms1);
983-
sret = qc_send(qc, 1, &send_list);
1018+
sret = qc_send(qc, 1, &send_list, 0);
9841019
qc_free_frm_list(qc, &frms1);
985-
if (!sret) {
1020+
if (sret < 0) {
9861021
qc_free_frm_list(qc, &frms2);
9871022
goto leave;
9881023
}
@@ -991,9 +1026,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
9911026
if (!LIST_ISEMPTY(&frms2)) {
9921027
apktns->tx.pto_probe = 1;
9931028
qel_register_send(&send_list, qc->ael, &frms2);
994-
sret = qc_send(qc, 1, &send_list);
1029+
sret = qc_send(qc, 1, &send_list, 0);
9951030
qc_free_frm_list(qc, &frms2);
996-
if (!sret)
1031+
if (sret < 0)
9971032
goto leave;
9981033
}
9991034
TRACE_STATE("no more need to probe 01RTT packet number space",

0 commit comments

Comments
 (0)