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 <tx.msd_frm>.

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.
This commit is contained in:
Amaury Denoyelle 2025-04-28 15:36:44 +02:00
parent 14a3fb679f
commit df50d3e39f
2 changed files with 65 additions and 7 deletions

View File

@ -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;

View File

@ -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 <qcc> 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 <qcc> 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;
}