From df50d3e39f03828198938cc618fad839d35fb908 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Mon, 28 Apr 2025 15:36:44 +0200 Subject: [PATCH] MINOR: mux-quic: limit emitted MSD frames count per qcs The previous commit has implemented a new calcul method for MAX_STREAM_DATA frame emission. Now, a frame may be emitted as soon as a buffer was consumed by a QCS instance. This will probably increase the number of MAX_STREAM_DATA frame emission. It may even cause a series of frame emitted for the same stream with increasing values under high load, which is completely unnecessary. To improve this, limit the number of MAX_STREAM_DATA frames built to one per QCS instance. This is implemented by storing a reference to this frame in QCS structure via a new member . Note that to properly reset QCS msd_frm member, emission of flow-control frames have been changed. Now, each frame is emitted individually. On one side, it is better as it prevent to emit frames related to different streams in a single datagram, which is not desirable in case of packet loss. However, this can also increase sendto() syscall invocation. --- include/haproxy/mux_quic-t.h | 1 + src/mux_quic.c | 71 ++++++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index 46f671329..1c7c19a32 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -160,6 +160,7 @@ struct qcs { } rx; struct { struct quic_fctl fc; /* stream flow control applied on sending */ + struct quic_frame *msd_frm; /* MAX_STREAM_DATA frame prepared */ } tx; struct eb64_node by_id; diff --git a/src/mux_quic.c b/src/mux_quic.c index 329477413..0bc882e9c 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -162,6 +162,8 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type) qfctl_init(&qcs->tx.fc, 0); } + qcs->tx.msd_frm = NULL; + qcs->rx.bufs = EB_ROOT_UNIQUE; qcs->rx.app_buf = BUF_NULL; qcs->rx.offset = qcs->rx.offset_max = 0; @@ -1237,20 +1239,25 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf } 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); + frm = qcs->tx.msd_frm; if (!frm) { - qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0); - return; + frm = qc_frm_alloc(QUIC_FT_MAX_STREAM_DATA); + if (!frm) { + qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0); + return; + } + + frm->max_stream_data.id = qcs->id; + LIST_APPEND(&qcc->lfctl.frms, &frm->list); + qcs->tx.msd_frm = frm; } + TRACE_DATA("increase stream credit via MAX_STREAM_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs); 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; - LIST_APPEND(&qcc->lfctl.frms, &frm->list); tasklet_wakeup(qcc->wait_event.tasklet); } @@ -2585,6 +2592,56 @@ static int qcs_send(struct qcs *qcs, struct list *frms, uint64_t window_conn) return -1; } +/* Emit prepared flow-control related frames from list. + * + * Returns 0 on success else non zero. + */ +static int qcc_emit_fctl(struct qcc *qcc) +{ + struct list single_frm = LIST_HEAD_INIT(single_frm); + struct quic_frame *frm; + struct eb64_node *node; + struct qcs *qcs; + int64_t id; + + TRACE_ENTER(QMUX_EV_QCC_END, qcc->conn); + + while (!LIST_ISEMPTY(&qcc->lfctl.frms)) { + /* Each frame is sent individually in a single element list. */ + frm = LIST_ELEM(qcc->lfctl.frms.n, struct quic_frame *, list); + id = frm->type == QUIC_FT_MAX_STREAM_DATA ? + frm->max_stream_data.id : -1; + + LIST_DELETE(&frm->list); + LIST_APPEND(&single_frm, &frm->list); + + if (qcc_send_frames(qcc, &single_frm, 0)) { + /* replace frame that cannot be sent in qcc list. */ + LIST_DELETE(&frm->list); + LIST_INSERT(&qcc->lfctl.frms, &frm->list); + goto err; + } + + BUG_ON_HOT(!LIST_ISEMPTY(&single_frm)); + + /* If frame is MAX_STREAM_DATA, reset corresponding QCS msd_frm pointer. */ + if (id >= 0) { + node = eb64_lookup(&qcc->streams_by_id, frm->max_stream_data.id); + if (node) { + qcs = eb64_entry(node, struct qcs, by_id); + qcs->tx.msd_frm = NULL; + } + } + } + + TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn); + return 0; + + err: + TRACE_DEVEL("leaving on error", QMUX_EV_QCC_SEND, qcc->conn); + return 1; +} + /* Send RESET_STREAM/STOP_SENDING for streams in send_list if requested. * Each frame is encoded and emitted separately for now. If a frame cannot be * sent, send_list looping is interrupted. @@ -2830,7 +2887,7 @@ static int qcc_io_send(struct qcc *qcc) } if (!LIST_ISEMPTY(&qcc->lfctl.frms)) { - if (qcc_send_frames(qcc, &qcc->lfctl.frms, 0)) { + if (qcc_emit_fctl(qcc)) { TRACE_DEVEL("flow-control frames rejected by transport, aborting send", QMUX_EV_QCC_SEND, qcc->conn); goto out; }