From 2873a31c81dfcc0a2b85d0c70f530bd3edf9b995 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Wed, 8 Dec 2021 14:42:55 +0100 Subject: [PATCH] MINOR: mux-quic: do not release qcs if there is remaining data to send A qcs is not freed if there is remaining data in its buffer. In this case, the flag QC_SF_DETACH is positionned. The qcc io handler is responsible to remove the qcs if the QC_SF_DETACH is set and their buffers are empty. --- include/haproxy/mux_quic-t.h | 1 + src/mux_quic.c | 149 ++++++++++++++++++++++------------- 2 files changed, 94 insertions(+), 56 deletions(-) diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index 2c5c55538..f41918da9 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -56,6 +56,7 @@ struct qcc { #define QC_SF_NONE 0x00000000 #define QC_SF_FIN_STREAM 0x00000001 // FIN bit must be set for last frame of the stream #define QC_SF_BLK_MROOM 0x00000002 // app layer is blocked waiting for room in the qcs.tx.buf +#define QC_SF_DETACH 0x00000004 // cs is detached but there is remaining data to send struct qcs { struct qcc *qcc; diff --git a/src/mux_quic.c b/src/mux_quic.c index 981235520..9111901c4 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -117,6 +117,62 @@ void qcs_notify_send(struct qcs *qcs) } } +/* detachs the QUIC stream from its QCC and releases it to the QCS pool. */ +static void qcs_destroy(struct qcs *qcs) +{ + fprintf(stderr, "%s: release stream %llu\n", __func__, qcs->by_id.key); + + eb64_delete(&qcs->by_id); + + b_free(&qcs->rx.buf); + b_free(&qcs->tx.buf); + b_free(&qcs->tx.xprt_buf); + + --qcs->qcc->strms[qcs_id_type(qcs->by_id.key)].nb_streams; + + pool_free(pool_head_qcs, qcs); +} + +static inline int qcc_is_dead(const struct qcc *qcc) +{ + fprintf(stderr, "%s: %lu\n", __func__, qcc->strms[QCS_CLT_BIDI].nb_streams); + + if (!qcc->strms[QCS_CLT_BIDI].nb_streams) + return 1; + + return 0; +} + +/* release function. This one should be called to free all resources allocated + * to the mux. + */ +static void qc_release(struct qcc *qcc) +{ + struct connection *conn = NULL; + + if (qcc) { + /* The connection must be aattached to this mux to be released */ + if (qcc->conn && qcc->conn->ctx == qcc) + conn = qcc->conn; + + if (qcc->wait_event.tasklet) + tasklet_free(qcc->wait_event.tasklet); + + pool_free(pool_head_qcc, qcc); + } + + if (conn) { + conn->mux = NULL; + conn->ctx = NULL; + + conn_stop_tracking(conn); + conn_full_close(conn); + if (conn->destroy_cb) + conn->destroy_cb(conn); + conn_free(conn); + } +} + static int qcs_push_frame(struct qcs *qcs, struct buffer *payload, int fin, uint64_t offset) { struct quic_frame *frm; @@ -208,6 +264,31 @@ static int qc_send(struct qcc *qcc) return ret; } +static int qc_release_detached_streams(struct qcc *qcc) +{ + struct eb64_node *node; + int release = 0; + + node = eb64_first(&qcc->streams_by_id); + while (node) { + struct qcs *qcs = container_of(node, struct qcs, by_id); + node = eb64_next(node); + + if (qcs->flags & QC_SF_DETACH) { + if (!b_data(&qcs->tx.buf) && !b_data(&qcs->tx.xprt_buf)) { + qcs_destroy(qcs); + release = 1; + } + else { + qcc->conn->xprt->subscribe(qcc->conn, qcc->conn->xprt_ctx, + SUB_RETRY_SEND, &qcc->wait_event); + } + } + } + + return release; +} + static struct task *qc_io_cb(struct task *t, void *ctx, unsigned int status) { struct qcc *qcc = ctx; @@ -216,6 +297,13 @@ static struct task *qc_io_cb(struct task *t, void *ctx, unsigned int status) qc_send(qcc); + if (qc_release_detached_streams(qcc)) { + if (qcc_is_dead(qcc)) { + qc_release(qcc); + return NULL; + } + } + return NULL; } @@ -264,62 +352,6 @@ static int qc_init(struct connection *conn, struct proxy *prx, return -1; } -/* detachs the QUIC stream from its QCC and releases it to the QCS pool. */ -static void qcs_destroy(struct qcs *qcs) -{ - fprintf(stderr, "%s: release stream %llu\n", __func__, qcs->by_id.key); - - eb64_delete(&qcs->by_id); - - b_free(&qcs->rx.buf); - b_free(&qcs->tx.buf); - b_free(&qcs->tx.xprt_buf); - - --qcs->qcc->strms[qcs_id_type(qcs->by_id.key)].nb_streams; - - pool_free(pool_head_qcs, qcs); -} - -static inline int qcc_is_dead(const struct qcc *qcc) -{ - fprintf(stderr, "%s: %lu\n", __func__, qcc->strms[QCS_CLT_BIDI].nb_streams); - - if (!qcc->strms[QCS_CLT_BIDI].nb_streams) - return 1; - - return 0; -} - -/* release function. This one should be called to free all resources allocated - * to the mux. - */ -static void qc_release(struct qcc *qcc) -{ - struct connection *conn = NULL; - - if (qcc) { - /* The connection must be aattached to this mux to be released */ - if (qcc->conn && qcc->conn->ctx == qcc) - conn = qcc->conn; - - if (qcc->wait_event.tasklet) - tasklet_free(qcc->wait_event.tasklet); - - pool_free(pool_head_qcc, qcc); - } - - if (conn) { - conn->mux = NULL; - conn->ctx = NULL; - - conn_stop_tracking(conn); - conn_full_close(conn); - if (conn->destroy_cb) - conn->destroy_cb(conn); - conn_free(conn); - } -} - static void qc_detach(struct conn_stream *cs) { struct qcs *qcs = cs->ctx; @@ -328,6 +360,11 @@ static void qc_detach(struct conn_stream *cs) fprintf(stderr, "%s: leaving with tx.buf.data=%lu, tx.xprt_buf.data=%lu\n", __func__, b_data(&qcs->tx.buf), b_data(&qcs->tx.xprt_buf)); + if (b_data(&qcs->tx.buf) || b_data(&qcs->tx.xprt_buf)) { + qcs->flags |= QC_SF_DETACH; + return; + } + qcs_destroy(qcs); if (qcc_is_dead(qcc)) { qc_release(qcc);