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;
}
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);
}
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.
}
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;
}