Skip to content

Commit 805fee3

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 fbf5b18 commit 805fee3

File tree

3 files changed

+70
-35
lines changed

3 files changed

+70
-35
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 & 32 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,7 +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;
543-
uchar gso_dgram_cnt = 0;
546+
uchar dgram_cnt = 0, gso_dgram_cnt = 0;
544547

545548
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
546549
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
@@ -588,6 +591,15 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
588591

589592
TRACE_PROTO("TX prep pkts", QUIC_EV_CONN_PHPKTS, qc, qel);
590593

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

@@ -655,8 +667,10 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
655667
* except if it is an too short Initial.
656668
*/
657669
if (first_pkt && (first_pkt->type != QUIC_PACKET_TYPE_INITIAL ||
658-
wrlen >= QUIC_INITIAL_PACKET_MINLEN))
670+
wrlen >= QUIC_INITIAL_PACKET_MINLEN)) {
659671
qc_txb_store(buf, wrlen, first_pkt);
672+
++dgram_cnt;
673+
}
660674
TRACE_PROTO("could not prepare anymore packet", QUIC_EV_CONN_PHPKTS, qc, qel);
661675
break;
662676

@@ -725,6 +739,8 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
725739
prv_pkt = cur_pkt;
726740
dglen = 0;
727741

742+
++dgram_cnt;
743+
728744
/* man 7 udp UDP_SEGMENT
729745
* The segment size must be chosen such that at
730746
* most 64 datagrams are sent in a single call
@@ -738,6 +754,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
738754
wrlen = dglen = 0;
739755
padding = 0;
740756
prv_pkt = NULL;
757+
++dgram_cnt;
741758
gso_dgram_cnt = 0;
742759
}
743760

@@ -761,7 +778,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
761778
}
762779

763780
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
764-
return total;
781+
return dgram_cnt;
765782

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

784809
TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
785810

786811
buf = qc_get_txb(qc);
787812
if (!buf) {
788813
TRACE_ERROR("buffer allocation failed", QUIC_EV_CONN_TXPKT, qc);
814+
ret = -1;
789815
goto out;
790816
}
791817

792818
if (b_data(buf) && !qc_purge_txbuf(qc, buf)) {
793819
TRACE_ERROR("Could not purge TX buffer", QUIC_EV_CONN_TXPKT, qc);
820+
ret = -1;
794821
goto out;
795822
}
796823

@@ -806,25 +833,36 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
806833
while (!LIST_ISEMPTY(send_list) &&
807834
(!(qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_DRAINING)) ||
808835
(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))) {
836+
809837
/* Buffer must always be empty before qc_prep_pkts() usage.
810838
* qc_send_ppkts() ensures it is cleared on success.
811839
*/
812840
BUG_ON_HOT(b_data(buf));
813841
b_reset(buf);
814842

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

817846
if (b_data(buf) && !qc_send_ppkts(buf, qc->xprt_ctx)) {
818847
if (qc->flags & QUIC_FL_CONN_TO_KILL)
819848
qc_txb_release(qc);
849+
ret = -1;
820850
goto out;
821851
}
822852

823-
if (ret <= 0) {
853+
if (prep <= 0) {
854+
/* TODO should this be considered error if prep<0 ? */
824855
TRACE_DEVEL("stopping on qc_prep_pkts() return", QUIC_EV_CONN_TXPKT, qc);
825856
break;
826857
}
827858

859+
ret += prep;
860+
BUG_ON(max_dgrams && ret > max_dgrams);
861+
if (max_dgrams && ret == max_dgrams && !LIST_ISEMPTY(send_list)) {
862+
TRACE_DEVEL("stopping for artificial pacing", QUIC_EV_CONN_TXPKT, qc);
863+
break;
864+
}
865+
828866
if ((qc->flags & QUIC_FL_CONN_DRAINING) &&
829867
!(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)) {
830868
TRACE_DEVEL("draining connection", QUIC_EV_CONN_TXPKT, qc);
@@ -833,10 +871,6 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
833871
}
834872

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

841875
out:
842876
if (old_data) {
@@ -850,8 +884,8 @@ int qc_send(struct quic_conn *qc, int old_data, struct list *send_list)
850884
qel->send_frms = NULL;
851885
}
852886

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

857891
/* Insert <qel> into <send_list> in preparation for sending. Set its send
@@ -915,10 +949,10 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
915949
if (qc->hel)
916950
qel_register_send(&send_list, qc->hel, &hfrms);
917951

918-
sret = qc_send(qc, 1, &send_list);
952+
sret = qc_send(qc, 1, &send_list, 0);
919953
qc_free_frm_list(qc, &ifrms);
920954
qc_free_frm_list(qc, &hfrms);
921-
if (!sret)
955+
if (sret < 0)
922956
goto leave;
923957
}
924958
else {
@@ -928,10 +962,10 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
928962
*/
929963
ipktns->tx.pto_probe = 1;
930964
qel_register_send(&send_list, qc->iel, &ifrms);
931-
sret = qc_send(qc, 0, &send_list);
965+
sret = qc_send(qc, 0, &send_list, 0);
932966
qc_free_frm_list(qc, &ifrms);
933967
qc_free_frm_list(qc, &hfrms);
934-
if (!sret)
968+
if (sret < 0)
935969
goto leave;
936970

937971
break;
@@ -957,9 +991,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
957991
if (!LIST_ISEMPTY(&frms1)) {
958992
hpktns->tx.pto_probe = 1;
959993
qel_register_send(&send_list, qc->hel, &frms1);
960-
sret = qc_send(qc, 1, &send_list);
994+
sret = qc_send(qc, 1, &send_list, 0);
961995
qc_free_frm_list(qc, &frms1);
962-
if (!sret)
996+
if (sret < 0)
963997
goto leave;
964998
}
965999
}
@@ -980,9 +1014,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
9801014
if (!LIST_ISEMPTY(&frms1)) {
9811015
apktns->tx.pto_probe = 1;
9821016
qel_register_send(&send_list, qc->ael, &frms1);
983-
sret = qc_send(qc, 1, &send_list);
1017+
sret = qc_send(qc, 1, &send_list, 0);
9841018
qc_free_frm_list(qc, &frms1);
985-
if (!sret) {
1019+
if (sret < 0) {
9861020
qc_free_frm_list(qc, &frms2);
9871021
goto leave;
9881022
}
@@ -991,9 +1025,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
9911025
if (!LIST_ISEMPTY(&frms2)) {
9921026
apktns->tx.pto_probe = 1;
9931027
qel_register_send(&send_list, qc->ael, &frms2);
994-
sret = qc_send(qc, 1, &send_list);
1028+
sret = qc_send(qc, 1, &send_list, 0);
9951029
qc_free_frm_list(qc, &frms2);
996-
if (!sret)
1030+
if (sret < 0)
9971031
goto leave;
9981032
}
9991033
TRACE_STATE("no more need to probe 01RTT packet number space",

0 commit comments

Comments
 (0)