From: Amaury Denoyelle Date: Wed, 19 Mar 2025 15:09:08 +0000 (+0100) Subject: MEDIUM: mux-quic: increase flow-control on each bufsize X-Git-Tag: v3.2-dev13~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=14a3fb679ff50188f83411b6fdc739bb8c77c220;p=thirdparty%2Fhaproxy.git MEDIUM: mux-quic: increase flow-control on each bufsize Recently, QCS Rx allocation buffer method has been improved. It is now possible to allocate multiple buffers per QCS instances, which was necessary to improve HTTP/3 POST throughput. However, a limitation remained related to the emission of MAX_STREAM_DATA. These frames are only emitted once at least half of the receive capacity has been consumed by its QCS instance. This may be too restrictive when a client need to upload a large payload. Improve this by adjusting MAX_STREAM_DATA allocation. If QCS capacity is still limited to 1 or 2 buffers max, the old calcul is still used. This is necessary when user has limited upload throughput via their configuration. If QCS capacity is more than 2 buffers, a new frame is emitted if at least a buffer was consumed. This patch has reduced number of STREAM_DATA_BLOCKED frames received in POST tests with some specific clients. --- diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index d87e2fecd..46f671329 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -156,7 +156,7 @@ struct qcs { struct eb_root bufs; /* receive buffers tree ordered by offset */ struct buffer app_buf; /* receive buffer used by stconn layer */ uint64_t msd; /* current max-stream-data limit to enforce */ - uint64_t msd_init; /* initial max-stream-data */ + uint64_t msd_base; /* max-stream-data previous to latest update */ } rx; struct { struct quic_fctl fc; /* stream flow control applied on sending */ diff --git a/src/mux_quic.c b/src/mux_quic.c index 89b24a573..329477413 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -173,7 +173,7 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type) else if (quic_stream_is_remote(qcc, id)) { qcs->rx.msd = qcc->lfctl.msd_uni_r; } - qcs->rx.msd_init = qcs->rx.msd; + qcs->rx.msd_base = 0; qcs->wait_event.tasklet = NULL; qcs->wait_event.events = 0; @@ -1198,6 +1198,7 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf struct qcc *qcc = qcs->qcc; struct quic_frame *frm; enum ncb_ret ret; + uint64_t diff, inc = 0; TRACE_ENTER(QMUX_EV_QCS_RECV, qcc->conn, qcs); @@ -1218,7 +1219,24 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf if (qcs->flags & QC_SF_SIZE_KNOWN) goto conn_fctl; - if (qcs->rx.msd - qcs->rx.offset < qcs->rx.msd_init / 2) { + /* Check if a MAX_STREAM_DATA frame should be emitted, determined by + * the consumed capacity. If no more than 2 Rx buffers can be allocated + * per QCS, the limit is set to half the capacity. Else, the limit is + * set to match bufsize. + */ + if (qcs->rx.msd - qcs->rx.msd_base < qmux_stream_rx_bufsz() * 2) { + if ((qcs->rx.offset - qcs->rx.msd_base) * 2 >= qcs->rx.msd - qcs->rx.msd_base) + inc = qcs->rx.offset - qcs->rx.msd_base; + } + else { + diff = qcs->rx.offset - qcs->rx.msd_base; + while (diff >= qmux_stream_rx_bufsz()) { + inc += qmux_stream_rx_bufsz(); + diff -= qmux_stream_rx_bufsz(); + } + } + + if (inc) { TRACE_DATA("increase stream credit via MAX_STREAM_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs); frm = qc_frm_alloc(QUIC_FT_MAX_STREAM_DATA); if (!frm) { @@ -1226,7 +1244,8 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf return; } - qcs->rx.msd = qcs->rx.offset + qcs->rx.msd_init; + qcs->rx.msd += inc; + qcs->rx.msd_base += inc; frm->max_stream_data.id = qcs->id; frm->max_stream_data.max_stream_data = qcs->rx.msd;