]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-quic: use send-list for STOP_SENDING/RESET_STREAM emission
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 6 Jan 2023 16:43:11 +0000 (17:43 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 10 Jan 2023 16:49:50 +0000 (17:49 +0100)
When a STOP_SENDING or RESET_STREAM must be send, its corresponding qcs
is inserted into <qcc.send_list> via qcc_reset_stream() or
qcc_abort_stream_read().

This allows to remove the iteration on full qcs tree in qc_send().
Instead, STOP_SENDING and RESET_STREAM is done in the loop over
<qcc.send_list> as with STREAM frames. This should improve slightly the
performance, most notably when large number of streams are opened.

This must be backported up to 2.7.

include/haproxy/mux_quic-t.h
src/mux_quic.c

index 256cdd93a1cbe0b8de25cc9b6b129108a73f0719..43a7cccb8f4b6e5aeddc74992c8a4ae359de19a3 100644 (file)
@@ -95,7 +95,7 @@ struct qcc {
        struct eb_root streams_by_id; /* all active streams by their ID */
 
        struct list send_retry_list; /* list of qcs eligible to send retry */
-       struct list send_list; /* list of qcs ready to send */
+       struct list send_list; /* list of qcs ready to send (STREAM, STOP_SENDING or RESET_STREAM emission) */
 
        struct wait_event wait_event;  /* To be used if we're waiting for I/Os */
 
index bb30f588ad53f74c8d91fe63b22162b28a2cf02f..195ba9f2a8b3c7c26b40643ac96c5dcbd84a3734 100644 (file)
@@ -818,6 +818,8 @@ void qcc_reset_stream(struct qcs *qcs, int err)
        TRACE_STATE("reset stream", QMUX_EV_QCS_END, qcc->conn, qcs);
        qcs->flags |= QC_SF_TO_RESET;
        qcs->err = err;
+
+       qcc_send_stream(qcs, 1);
        tasklet_wakeup(qcc->wait_event.tasklet);
 }
 
@@ -858,6 +860,8 @@ void qcc_abort_stream_read(struct qcs *qcs)
 
        TRACE_STATE("abort stream read", QMUX_EV_QCS_END, qcc->conn, qcs);
        qcs->flags |= (QC_SF_TO_STOP_SENDING|QC_SF_READ_ABORTED);
+
+       qcc_send_stream(qcs, 1);
        tasklet_wakeup(qcc->wait_event.tasklet);
 
  end:
@@ -1445,6 +1449,13 @@ static int qcs_stream_fin(struct qcs *qcs)
        return qcs->flags & QC_SF_FIN_STREAM && !b_data(&qcs->tx.buf);
 }
 
+/* Return true if <qcs> has data to send in new STREAM frames. */
+static forceinline int qcs_need_sending(struct qcs *qcs)
+{
+       return b_data(&qcs->tx.buf) || qcs->tx.sent_offset < qcs->tx.offset ||
+              qcs_stream_fin(qcs);
+}
+
 /* This function must be called by the upper layer to inform about the sending
  * of a STREAM frame for <qcs> instance. The frame is of <data> length and on
  * <offset>.
@@ -1713,7 +1724,6 @@ static int _qc_send_qcs(struct qcs *qcs, struct list *frms)
 static int qc_send(struct qcc *qcc)
 {
        struct list frms = LIST_HEAD_INIT(frms);
-       struct eb64_node *node;
        struct qcs *qcs, *qcs_tmp;
        int total = 0, tmp_total = 0;
 
@@ -1733,7 +1743,7 @@ static int qc_send(struct qcc *qcc)
        }
 
        if (qcc->flags & QC_CF_BLK_MFCTL)
-               return 0;
+               goto err;
 
        if (!(qcc->flags & QC_CF_APP_FINAL) && !eb_is_empty(&qcc->streams_by_id) &&
            qcc->app_ops->finalize) {
@@ -1744,43 +1754,41 @@ static int qc_send(struct qcc *qcc)
                qcc->flags |= QC_CF_APP_FINAL;
        }
 
-       /* Loop through all streams for STOP_SENDING/RESET_STREAM sending. Each
-        * frame is send individually to guarantee emission.
-        *
-        * TODO Optimize sending by multiplexing several frames in one datagram.
-        */
-       node = eb64_first(&qcc->streams_by_id);
-       while (node) {
-               uint64_t id;
+       /* Send STREAM/STOP_SENDING/RESET_STREAM data for registered streams. */
+       list_for_each_entry_safe(qcs, qcs_tmp, &qcc->send_list, el_send) {
+               /* Stream must not be present in send_list if it has nothing to send. */
+               BUG_ON(!(qcs->flags & (QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET)) &&
+                      !qcs_need_sending(qcs));
 
-               qcs = eb64_entry(node, struct qcs, by_id);
-               id = qcs->id;
+               /* Each STOP_SENDING/RESET_STREAM frame is sent individually to
+                * guarantee its emission.
+                *
+                * TODO multiplex several frames in same datagram to optimize sending
+                */
+               if (qcs->flags & QC_SF_TO_STOP_SENDING) {
+                       if (qcs_send_stop_sending(qcs))
+                               goto out;
 
-               if (quic_stream_is_uni(id) && quic_stream_is_remote(qcc, id)) {
-                       node = eb64_next(node);
-                       continue;
+                       /* Remove stream from send_list if it had only STOP_SENDING
+                        * to send.
+                        */
+                       if (!(qcs->flags & QC_SF_TO_RESET) && !qcs_need_sending(qcs)) {
+                               LIST_DEL_INIT(&qcs->el_send);
+                               continue;
+                       }
                }
 
-               if (qcs->flags & QC_SF_TO_STOP_SENDING)
-                       qcs_send_stop_sending(qcs);
-
                if (qcs->flags & QC_SF_TO_RESET) {
-                       qcs_send_reset(qcs);
-                       node = eb64_next(node);
-                       continue;
-               }
-
-               node = eb64_next(node);
-       }
+                       if (qcs_send_reset(qcs))
+                               goto out;
 
-       /* Send STREAM data for registered streams. */
-       list_for_each_entry(qcs, &qcc->send_list, el_send) {
-               /* Stream must not be present in send_list if it has nothing to send. */
-               BUG_ON(!b_data(&qcs->tx.buf) &&
-                      qcs->tx.sent_offset == qcs->tx.offset &&
-                      !qcs_stream_fin(qcs));
-
-               if (qcs_is_close_local(qcs)) {
+                       /* RFC 9000 3.3. Permitted Frame Types
+                        *
+                        * A sender MUST NOT send
+                        * a STREAM or STREAM_DATA_BLOCKED frame for a stream in the
+                        * "Reset Sent" state or any terminal state -- that is, after
+                        * sending a RESET_STREAM frame.
+                        */
                        LIST_DEL_INIT(&qcs->el_send);
                        continue;
                }