]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM/OPTIM: mux-quic: implement purg_list
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 17 Dec 2024 10:16:34 +0000 (11:16 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 18 Dec 2024 08:33:52 +0000 (09:33 +0100)
This commit is part of the current serie which aims to refactor and
improve overall performance of QUIC MUX I/O handler.

qcc_io_process() is responsible to perform some internal operations on
QUIC MUX after I/O completion. It is notably called on every qcc_io_cb()
tasklet handler.

The most intensive work on it is the purging of QCS instances after
transfer completion. This was implemented by looping on QCC streams tree
and inspecting the state of every QCS. The purpose of this commit is to
optimize this processing.

A new purg_list QCC member is defined. It is responsible to list every
QCS instances whose transfer has been completed. It is thus safe to
reuse <el_send> QCS list attach point. Stream purging will thus only
loop on purg_list instead of every known QCS.

This should be backported up to 3.1.

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

index ee145de403e29ab1f6c2faebde50c5428f913a30..1f69d8838faae8288a188255484926eb106ece3f 100644 (file)
@@ -86,6 +86,7 @@ struct qcc {
        struct list send_list; /* list of qcs ready to send (STREAM, STOP_SENDING or RESET_STREAM emission) */
        struct list fctl_list; /* list of sending qcs blocked on conn flow control */
        struct list buf_wait_list; /* list of qcs blocked on stream desc buf */
+       struct list purg_list; /* list of qcs which can be purged */
 
        struct wait_event wait_event;  /* To be used if we're waiting for I/Os */
 
index 4695d160702c6cf111555df05437508ea6f878a4..59c474e1e8b30daf28d66c5c946f249a937eaeda 100644 (file)
@@ -456,6 +456,16 @@ static void qcs_close_local(struct qcs *qcs)
        }
 }
 
+/* Returns true if <qcs> can be purged. */
+static int qcs_is_completed(struct qcs *qcs)
+{
+       /* A stream is completed if fully closed and stconn released, or simply
+        * detached and everything already sent.
+        */
+       return (qcs->st == QC_SS_CLO && !qcs_sc(qcs)) ||
+              (qcs_is_close_local(qcs) && (qcs->flags & QC_SF_DETACH));
+}
+
 /* Close the remote channel of <qcs> instance. */
 static void qcs_close_remote(struct qcs *qcs)
 {
@@ -475,6 +485,12 @@ static void qcs_close_remote(struct qcs *qcs)
                BUG_ON_HOT(quic_stream_is_local(qcs->qcc, qcs->id));
                qcs->st = QC_SS_CLO;
        }
+
+       if (qcs_is_completed(qcs)) {
+               BUG_ON(LIST_INLIST(&qcs->el_send));
+               TRACE_STATE("add stream in purg_list", QMUX_EV_QCS_RECV, qcs->qcc->conn, qcs);
+               LIST_APPEND(&qcs->qcc->purg_list, &qcs->el_send);
+       }
 }
 
 int qcs_is_close_local(struct qcs *qcs)
@@ -661,6 +677,11 @@ static void qmux_ctrl_send(struct qc_stream_desc *stream, uint64_t data, uint64_
 
                        /* Unsubscribe from streamdesc when everything sent. */
                        qc_stream_desc_sub_send(qcs->stream, NULL);
+
+                       if (qcs_is_completed(qcs)) {
+                               TRACE_STATE("add stream in purg_list", QMUX_EV_QCS_SEND, qcc->conn, qcs);
+                               LIST_APPEND(&qcc->purg_list, &qcs->el_send);
+                       }
                }
        }
 
@@ -2372,6 +2393,10 @@ static int qcc_io_send(struct qcc *qcc)
                         * sending a RESET_STREAM frame.
                         */
                        LIST_DEL_INIT(&qcs->el_send);
+                       if (qcs_is_completed(qcs)) {
+                               TRACE_STATE("add stream in purg_list", QMUX_EV_QCC_SEND|QMUX_EV_QCS_SEND, qcc->conn, qcs);
+                               LIST_APPEND(&qcc->purg_list, &qcs->el_send);
+                       }
                        continue;
                }
 
@@ -2545,43 +2570,22 @@ static int qcc_io_recv(struct qcc *qcc)
 }
 
 
-/* Release all streams which have their transfer operation achieved.
- *
- * Returns true if at least one stream is released.
- */
-static int qcc_purge_streams(struct qcc *qcc)
+/* Release all streams which have their transfer operation achieved. */
+static void qcc_purge_streams(struct qcc *qcc)
 {
-       struct eb64_node *node;
-       int release = 0;
+       struct qcs *qcs;
 
        TRACE_ENTER(QMUX_EV_QCC_WAKE, qcc->conn);
 
-       node = eb64_first(&qcc->streams_by_id);
-       while (node) {
-               struct qcs *qcs = eb64_entry(node, struct qcs, by_id);
-               node = eb64_next(node);
+       while (!LIST_ISEMPTY(&qcc->purg_list)) {
+               qcs = LIST_ELEM(qcc->purg_list.n, struct qcs *, el_send);
 
-               /* Release not attached closed streams. */
-               if (qcs->st == QC_SS_CLO && !qcs_sc(qcs)) {
-                       TRACE_STATE("purging closed stream", QMUX_EV_QCC_WAKE, qcs->qcc->conn, qcs);
-                       qcs_destroy(qcs);
-                       release = 1;
-                       continue;
-               }
-
-               /* Release detached streams with empty buffer. */
-               if (qcs->flags & QC_SF_DETACH) {
-                       if (qcs_is_close_local(qcs)) {
-                               TRACE_STATE("purging detached stream", QMUX_EV_QCC_WAKE, qcs->qcc->conn, qcs);
-                               qcs_destroy(qcs);
-                               release = 1;
-                               continue;
-                       }
-               }
+               TRACE_STATE("purging stream", QMUX_EV_QCC_WAKE, qcs->qcc->conn, qcs);
+               BUG_ON_HOT(!qcs_is_completed(qcs));
+               qcs_destroy(qcs);
        }
 
        TRACE_LEAVE(QMUX_EV_QCC_WAKE, qcc->conn);
-       return release;
 }
 
 /* Execute application layer shutdown. If this operation is not defined, a
@@ -2659,7 +2663,8 @@ static int qcc_wake_some_streams(struct qcc *qcc)
  */
 static int qcc_io_process(struct qcc *qcc)
 {
-       qcc_purge_streams(qcc);
+       if (!LIST_ISEMPTY(&qcc->purg_list))
+               qcc_purge_streams(qcc);
 
        /* Check if a soft-stop is in progress.
         *
@@ -2991,6 +2996,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        LIST_INIT(&qcc->send_list);
        LIST_INIT(&qcc->fctl_list);
        LIST_INIT(&qcc->buf_wait_list);
+       LIST_INIT(&qcc->purg_list);
 
        qcc->wait_event.tasklet->process = qcc_io_cb;
        qcc->wait_event.tasklet->context = qcc;