From: Amaury Denoyelle Date: Fri, 29 Nov 2024 15:18:12 +0000 (+0100) Subject: MINOR: mux-quic: refactor wait-for-handshake support X-Git-Tag: v3.2-dev2~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0a53a008d032b69377869c8caaec38f81bdd5bd6;p=thirdparty%2Fhaproxy.git MINOR: mux-quic: refactor wait-for-handshake support 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. --- diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index be55a5a7eb..251fe74960 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -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; diff --git a/src/mux_quic.c b/src/mux_quic.c index 8792d68431..7a254901e6 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -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 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 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;