]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-quic: refactor wait-for-handshake support
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 29 Nov 2024 15:18:12 +0000 (16:18 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 18 Dec 2024 08:23:41 +0000 (09:23 +0100)
This commit refactors wait-for-handshake support from QUIC MUX. The flag
logic QC_CF_WAIT_HS is inverted : it is now positionned only if MUX is
instantiated before handshake completion. When the handshake is
completed, the flag is removed.

The flag is now set directly on initialization via qmux_init(). Removal
via qcc_wait_for_hs() is moved from qcc_io_process() to qcc_io_recv().
This is deemed more logical as QUIC MUX is scheduled on RECV to be
notify by the transport layer about handshake termination. Moreover,
qcc_wait_for_hs() is now called if recv subscription is still active.

This commit is the first of a serie which aims to refactor QUIC MUX I/O
handler and improves its overall performance. The ultimate objective is
to be able to stream qcc_io_cb() by removing pacing specific code path
via qcc_purge_sending().

This should be backported up to 3.1.

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

index be55a5a7eb07951dae11fd0cac6436eac56bdfbd..251fe74960308d7686a1c0849cb4cb3513002dfc 100644 (file)
@@ -219,7 +219,7 @@ struct qcc_app_ops {
 #define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
 #define QC_CF_APP_SHUT  0x00000010 /* Application layer shutdown done. */
 #define QC_CF_ERR_CONN  0x00000020 /* fatal error reported by transport layer */
-#define QC_CF_WAIT_FOR_HS 0x00000040 /* QUIC handshake has been completed */
+#define QC_CF_WAIT_HS   0x00000040 /* MUX init before QUIC handshake completed (0-RTT) */
 
 /* This function is used to report flags in debugging tools. Please reflect
  * below any single-bit flag addition above in the same order via the
@@ -236,7 +236,7 @@ static forceinline char *qcc_show_flags(char *buf, size_t len, const char *delim
        _(QC_CF_CONN_FULL,
        _(QC_CF_APP_SHUT,
        _(QC_CF_ERR_CONN,
-       _(QC_CF_WAIT_FOR_HS))))));
+       _(QC_CF_WAIT_HS))))));
        /* epilogue */
        _(~0U);
        return buf;
index 8792d68431c886c8ce4d21969d474605046cb850..7a254901e6a0389c97a7315fd55773a43e73de24 100644 (file)
@@ -2476,6 +2476,39 @@ static int qcc_io_send(struct qcc *qcc)
        return total;
 }
 
+/* Detects QUIC handshake completion. Any SE_FL_WAIT_FOR_HS streams are woken
+ * up if wait-for-handshake is active.
+ */
+static void qcc_wait_for_hs(struct qcc *qcc)
+{
+       struct connection *conn = qcc->conn;
+       struct quic_conn *qc = conn->handle.qc;
+       struct eb64_node *node;
+       struct qcs *qcs;
+
+       TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn);
+
+       if (qc->state >= QUIC_HS_ST_COMPLETE) {
+               if (conn->flags & CO_FL_EARLY_SSL_HS) {
+                       TRACE_STATE("mark early data as ready", QMUX_EV_QCC_WAKE, conn);
+                       conn->flags &= ~CO_FL_EARLY_SSL_HS;
+               }
+
+               /* wake-up any stream blocked on early data transfer */
+               node = eb64_first(&qcc->streams_by_id);
+               while (node) {
+                       qcs = container_of(node, struct qcs, by_id);
+                       if (se_fl_test(qcs->sd, SE_FL_WAIT_FOR_HS))
+                               qcs_notify_recv(qcs);
+                       node = eb64_next(node);
+               }
+
+               qcc->flags &= ~QC_CF_WAIT_HS;
+       }
+
+       TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
+}
+
 /* Proceed on receiving. Loop through all streams from <qcc> and use decode_qcs
  * operation.
  *
@@ -2494,6 +2527,9 @@ static int qcc_io_recv(struct qcc *qcc)
                return 0;
        }
 
+       if ((qcc->flags & QC_CF_WAIT_HS) && !(qcc->wait_event.events & SUB_RETRY_RECV))
+               qcc_wait_for_hs(qcc);
+
        node = eb64_first(&qcc->streams_by_id);
        while (node) {
                uint64_t id;
@@ -2626,44 +2662,6 @@ static int qcc_wake_some_streams(struct qcc *qcc)
        return 0;
 }
 
-/* Checks whether QUIC handshake is still active or not. This is necessary to
- * mark that connection may convey early data to delay stream processing if
- * wait-for-handshake is active. On handshake completion, any SE_FL_WAIT_FOR_HS
- * streams are woken up to restart their processing.
- */
-static void qcc_wait_for_hs(struct qcc *qcc)
-{
-       struct connection *conn = qcc->conn;
-       struct quic_conn *qc = conn->handle.qc;
-       struct eb64_node *node;
-       struct qcs *qcs;
-
-       if (qc->state < QUIC_HS_ST_COMPLETE) {
-               if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
-                       TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
-                       conn->flags |= CO_FL_EARLY_SSL_HS;
-                       /* subscribe for handshake completion */
-                       conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
-                                             &qcc->wait_event);
-               }
-       }
-       else {
-               if (conn->flags & CO_FL_EARLY_SSL_HS) {
-                       TRACE_STATE("mark early data as ready", QMUX_EV_QCC_WAKE, conn);
-                       conn->flags &= ~CO_FL_EARLY_SSL_HS;
-               }
-               qcc->flags |= QC_CF_WAIT_FOR_HS;
-
-               node = eb64_first(&qcc->streams_by_id);
-               while (node) {
-                       qcs = container_of(node, struct qcs, by_id);
-                       if (se_fl_test(qcs->sd, SE_FL_WAIT_FOR_HS))
-                               qcs_notify_recv(qcs);
-                       node = eb64_next(node);
-               }
-       }
-}
-
 /* Conduct operations which should be made for <qcc> connection after
  * input/output. Most notably, closed streams are purged which may leave the
  * connection has ready to be released.
@@ -2674,9 +2672,6 @@ static int qcc_io_process(struct qcc *qcc)
 {
        qcc_purge_streams(qcc);
 
-       if (!(qcc->flags & QC_CF_WAIT_FOR_HS))
-               qcc_wait_for_hs(qcc);
-
        /* Check if a soft-stop is in progress.
         *
         * TODO this is relevant for frontend connections only.
@@ -3060,6 +3055,21 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        /* init read cycle */
        qcc_wakeup(qcc);
 
+       /* MUX is initialized before QUIC handshake completion if early data
+        * received. Flag connection to delay stream processing if
+        * wait-for-handshake is active.
+        */
+       if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
+               if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
+                       TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
+                       conn->flags |= CO_FL_EARLY_SSL_HS;
+                       /* subscribe for handshake completion */
+                       conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
+                                             &qcc->wait_event);
+                       qcc->flags |= QC_CF_WAIT_HS;
+               }
+       }
+
        TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
        return 0;