return !!(qc->path->cc.algo->pacing_rate);
}
-/* Free <qcc> STREAM frames in Tx list. */
-static void qcc_tx_frms_free(struct qcc *qcc)
-{
- while (!LIST_ISEMPTY(&qcc->tx.frms)) {
- struct quic_frame *frm = LIST_ELEM(qcc->tx.frms.n, struct quic_frame *, list);
- qc_frm_free(qcc->conn->handle.qc, &frm);
- }
-}
-
static void qcs_free_ncbuf(struct qcs *qcs, struct ncbuf *ncbuf)
{
struct buffer buf;
}
}
+/* Free <qcc> STREAM frames in Tx list. */
+static void qcc_clear_frms(struct qcc *qcc)
+{
+ while (!LIST_ISEMPTY(&qcc->tx.frms)) {
+ struct quic_frame *frm = LIST_ELEM(qcc->tx.frms.n, struct quic_frame *, list);
+ qc_frm_free(qcc->conn->handle.qc, &frm);
+ }
+}
+
/* Prepare for the emission of RESET_STREAM on <qcs> with error code <err>. */
void qcc_reset_stream(struct qcs *qcs, int err)
{
return -1;
}
-/* Proceed to sending. Loop through all available streams for the <qcc>
- * instance and try to send as much as possible.
+/* Encode STREAM frames into <qcc> tx frms for streams registered into
+ * send_list. On each error, related stream is removed from send_list and
+ * inserted into <qcs_failed> list.
*
- * Returns the total of bytes sent to the transport layer.
+ * This functions also serves to emit RESET_STREAM and STOP_SENDING frames. In
+ * this case, frame is emitted immediately without using <qcc> tx frms. If an
+ * error occured during this step, this is considered as fatal. Tx frms is
+ * cleared and 0 is returned.
+ *
+ * Returns the sum of encoded STREAM frames length or 0 if no frame built.
*/
-static int qcc_io_send(struct qcc *qcc)
+static int qcc_build_frms(struct qcc *qcc, struct list *qcs_failed)
{
struct list *frms = &qcc->tx.frms;
- /* Temporary list for QCS on error. */
- struct list qcs_failed = LIST_HEAD_INIT(qcs_failed);
struct qcs *qcs, *qcs_tmp, *first_qcs = NULL;
uint64_t window_conn = qfctl_rcap(&qcc->tx.fc);
- int ret = 0, ret_sent = 0, total = 0, resent;
-
- TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
-
- /* TODO if socket in transient error, sending should be temporarily
- * disabled for all frames. However, checking for send subscription is
- * not valid as this may be caused by a congestion error which only
- * apply for STREAM frames.
- */
-
- qcc_tx_frms_free(qcc);
+ int ret = 0, total = 0;
- /* Check for transport error. */
- if (qcc->flags & QC_CF_ERR_CONN || qcc->conn->flags & CO_FL_ERROR) {
- TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
- goto out;
- }
-
- /* Check for locally detected connection error. */
- if (qcc->flags & QC_CF_ERRL) {
- /* Prepare a CONNECTION_CLOSE if not already done. */
- if (!(qcc->flags & QC_CF_ERRL_DONE)) {
- TRACE_DATA("report a connection error", QMUX_EV_QCC_SEND|QMUX_EV_QCC_ERR, qcc->conn);
- quic_set_connection_close(qcc->conn->handle.qc, qcc->err);
- qcc->flags |= QC_CF_ERRL_DONE;
- }
- goto out;
- }
+ TRACE_ENTER(QMUX_EV_QCC_END, qcc->conn);
- if (qcc->conn->flags & CO_FL_SOCK_WR_SH) {
- qcc->conn->flags |= CO_FL_ERROR;
- TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
- goto out;
- }
+ /* Frames list must first be cleared via qcc_clear_frms(). */
+ BUG_ON(!LIST_ISEMPTY(&qcc->tx.frms));
- if (!LIST_ISEMPTY(&qcc->lfctl.frms)) {
- if (qcc_send_frames(qcc, &qcc->lfctl.frms, 0)) {
- TRACE_DEVEL("flow-control frames rejected by transport, aborting send", QMUX_EV_QCC_SEND, qcc->conn);
- goto out;
- }
- }
-
- /* Send STREAM/STOP_SENDING/RESET_STREAM data for registered streams. */
list_for_each_entry_safe(qcs, qcs_tmp, &qcc->send_list, el_send) {
/* Check if all QCS were processed. */
if (qcs == first_qcs)
*/
if (qcs->flags & QC_SF_TO_STOP_SENDING) {
if (qcs_send_stop_sending(qcs))
- goto sent_done;
+ goto err;
/* Remove stream from send_list if it had only STOP_SENDING
* to send.
if (qcs->flags & QC_SF_TO_RESET) {
if (qcs_send_reset(qcs))
- goto sent_done;
+ goto err;
/* RFC 9000 3.3. Permitted Frame Types
*
if ((ret = qcs_send(qcs, frms, window_conn - total)) < 0) {
/* Temporarily remove QCS from send-list. */
LIST_DEL_INIT(&qcs->el_send);
- LIST_APPEND(&qcs_failed, &qcs->el_send);
+ LIST_APPEND(qcs_failed, &qcs->el_send);
continue;
}
}
}
+ TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
+ return total;
+
+ err:
+ qcc_clear_frms(qcc);
+ TRACE_DEVEL("leaving on error", QMUX_EV_QCC_SEND, qcc->conn);
+ return 0;
+}
+
+/* Proceed to sending. Loop through all available streams for the <qcc>
+ * instance and try to send as much as possible.
+ *
+ * Returns the total of bytes sent to the transport layer.
+ */
+static int qcc_io_send(struct qcc *qcc)
+{
+ struct list *frms = &qcc->tx.frms;
+ /* Temporary list for QCS on error. */
+ struct list qcs_failed = LIST_HEAD_INIT(qcs_failed);
+ struct qcs *qcs, *qcs_tmp;
+ uint64_t window_conn = qfctl_rcap(&qcc->tx.fc);
+ int ret = 0, total = 0, resent;
+
+ TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
+
+ /* TODO if socket in transient error, sending should be temporarily
+ * disabled for all frames. However, checking for send subscription is
+ * not valid as this may be caused by a congestion error which only
+ * apply for STREAM frames.
+ */
+
+ qcc_clear_frms(qcc);
+
+ /* Check for transport error. */
+ if (qcc->flags & QC_CF_ERR_CONN || qcc->conn->flags & CO_FL_ERROR) {
+ TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
+ goto out;
+ }
+
+ /* Check for locally detected connection error. */
+ if (qcc->flags & QC_CF_ERRL) {
+ /* Prepare a CONNECTION_CLOSE if not already done. */
+ if (!(qcc->flags & QC_CF_ERRL_DONE)) {
+ TRACE_DATA("report a connection error", QMUX_EV_QCC_SEND|QMUX_EV_QCC_ERR, qcc->conn);
+ quic_set_connection_close(qcc->conn->handle.qc, qcc->err);
+ qcc->flags |= QC_CF_ERRL_DONE;
+ }
+ goto out;
+ }
+
+ if (qcc->conn->flags & CO_FL_SOCK_WR_SH) {
+ qcc->conn->flags |= CO_FL_ERROR;
+ TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
+ goto out;
+ }
+
+ if (!LIST_ISEMPTY(&qcc->lfctl.frms)) {
+ if (qcc_send_frames(qcc, &qcc->lfctl.frms, 0)) {
+ TRACE_DEVEL("flow-control frames rejected by transport, aborting send", QMUX_EV_QCC_SEND, qcc->conn);
+ goto out;
+ }
+ }
+
+ /* Send STREAM/STOP_SENDING/RESET_STREAM data for registered streams. */
+ total = qcc_build_frms(qcc, &qcs_failed);
+ if (!total)
+ goto sent_done;
+
if (qcc_is_pacing_active(qcc->conn)) {
if (!LIST_ISEMPTY(frms) && !quic_pacing_expired(&qcc->tx.pacer)) {
qcc_wakeup_pacing(qcc);
/* Retry sending until no frame to send, data rejected or connection
* flow-control limit reached.
*/
- while ((ret_sent = qcc_send_frames(qcc, frms, 1)) == 0 && !qfctl_rblocked(&qcc->tx.fc)) {
+ while ((ret = qcc_send_frames(qcc, frms, 1)) == 0 && !qfctl_rblocked(&qcc->tx.fc)) {
window_conn = qfctl_rcap(&qcc->tx.fc);
resent = 0;
}
sent_done:
- if (ret_sent == 1) {
+ if (ret == 1) {
/* qcc_send_frames cannot return 1 if pacing not used. */
BUG_ON(!qcc_is_pacing_active(qcc->conn));
qcc_wakeup_pacing(qcc);
}
else if (!LIST_ISEMPTY(&qcc->tx.frms)) {
/* Deallocate frames that the transport layer has rejected. */
- qcc_tx_frms_free(qcc);
+ qcc_clear_frms(qcc);
}
else {
/* Everything sent */
qc_frm_free(qcc->conn->handle.qc, &frm);
}
- qcc_tx_frms_free(qcc);
+ qcc_clear_frms(qcc);
if (qcc->app_ops && qcc->app_ops->release)
qcc->app_ops->release(qcc->ctx);