]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-quic: do not release qcs if there is remaining data to send
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 8 Dec 2021 13:42:55 +0000 (14:42 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 8 Dec 2021 14:26:16 +0000 (15:26 +0100)
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
src/mux_quic.c

index 2c5c5553806a94a894b110d4f18af22b9e71b2f8..f41918da9f3eb23a0d7c4a24f1ed8173682f41d4 100644 (file)
@@ -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;
index 981235520ca85e4d8642ed00ca892ada284c64e1..9111901c4230848c6fadfc7ff8c05a675096b773 100644 (file)
@@ -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);