MINOR: quic: create a global list dedicated for closing QUIC conns

When a CONNECTION_CLOSE is emitted or received, a QUIC connection enters
respectively in draining or closing state. These states are a loose
equivalent of TCP TIME_WAIT. No data can be exchanged anymore but the
connection is maintained during a certain timer to handle packet
reordering or loss.

A new global list has been defined for QUIC connections in
closing/draining state inside thread_ctx structure. Each time a
connection enters in one of this state, it will be moved from the
default global list to the new closing list.

The objective of this patch is to quickly filter connections on
closing/draining. Most notably, this will be used to wake up these
connections and avoid that haproxy process stopping is delayed by them.

A dedicated function qc_detach_th_ctx_list() has been implemented to
transfer a quic-conn from one list instance to the other. This takes
care of back-references attach to a quic-conn instance in case of a
running "show quic".

This should be backported up to 2.7.
diff --git a/include/haproxy/tinfo-t.h b/include/haproxy/tinfo-t.h
index a12e0a4..7c201af 100644
--- a/include/haproxy/tinfo-t.h
+++ b/include/haproxy/tinfo-t.h
@@ -132,7 +132,8 @@
 	struct list pool_lru_head;          /* oldest objects in thread-local pool caches */
 	struct list buffer_wq;              /* buffer waiters */
 	struct list streams;                /* list of streams attached to this thread */
-	struct list quic_conns;             /* list of quic-conns attached to this thread */
+	struct list quic_conns;             /* list of active quic-conns attached to this thread */
+	struct list quic_conns_clo;         /* list of closing quic-conns attached to this thread */
 
 	ALWAYS_ALIGN(2*sizeof(void*));
 	struct list tasklets[TL_CLASSES];   /* tasklets (and/or tasks) to run, by class */
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 90a2c5d..9a51766 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -3023,6 +3023,39 @@
 	return ret;
 }
 
+/* Remove a <qc> quic-conn from its ha_thread_ctx list. If <closing> is true,
+ * it will immediately be reinserted in the ha_thread_ctx quic_conns_clo list.
+ */
+static void qc_detach_th_ctx_list(struct quic_conn *qc, int closing)
+{
+	struct bref *bref, *back;
+
+	/* Detach CLI context watchers currently dumping this connection.
+	 * Reattach them to the next quic_conn instance.
+	 */
+	list_for_each_entry_safe(bref, back, &qc->back_refs, users) {
+		/* Remove watcher from this quic_conn instance. */
+		LIST_DEL_INIT(&bref->users);
+
+		/* Attach it to next instance unless it was the last list element. */
+		if (qc->el_th_ctx.n != &th_ctx->quic_conns &&
+		    qc->el_th_ctx.n != &th_ctx->quic_conns_clo) {
+			struct quic_conn *next = LIST_NEXT(&qc->el_th_ctx,
+			                                   struct quic_conn *,
+			                                   el_th_ctx);
+			LIST_APPEND(&next->back_refs, &bref->users);
+		}
+		bref->ref = qc->el_th_ctx.n;
+		__ha_barrier_store();
+	}
+
+	/* Remove quic_conn from global ha_thread_ctx list. */
+	LIST_DEL_INIT(&qc->el_th_ctx);
+
+	if (closing)
+		LIST_APPEND(&th_ctx->quic_conns_clo, &qc->el_th_ctx);
+}
+
 /* Parse all the frames of <pkt> QUIC packet for QUIC connection <qc> and <qel>
  * as encryption level.
  * Returns 1 if succeeded, 0 if failed.
@@ -3205,6 +3238,7 @@
 				 * state.
 				 */
 				qc->flags |= QUIC_FL_CONN_DRAINING|QUIC_FL_CONN_IMMEDIATE_CLOSE;
+				qc_detach_th_ctx_list(qc, 1);
 				qc_idle_timer_do_rearm(qc);
 				qc_notify_close(qc);
 			}
@@ -3730,6 +3764,7 @@
 			if (!(qc->flags & QUIC_FL_CONN_CLOSING) &&
 			    (pkt->flags & QUIC_FL_TX_PACKET_CC)) {
 				qc->flags |= QUIC_FL_CONN_CLOSING;
+				qc_detach_th_ctx_list(qc, 1);
 				qc_notify_close(qc);
 
 				/* RFC 9000 10.2. Immediate Close:
@@ -5438,7 +5473,6 @@
 	struct eb64_node *node;
 	struct quic_tls_ctx *app_tls_ctx;
 	struct quic_rx_packet *pkt, *pktback;
-	struct bref *bref, *back;
 
 	TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
 
@@ -5517,25 +5551,7 @@
 		quic_free_arngs(qc, &qc->pktns[i].rx.arngs);
 	}
 
-	/* Detach CLI context watchers currently dumping this connection.
-	 * Reattach them to the next quic_conn instance.
-	 */
-	list_for_each_entry_safe(bref, back, &qc->back_refs, users) {
-		/* Remove watcher from this quic_conn instance. */
-		LIST_DEL_INIT(&bref->users);
-
-		/* Attach it to next instance unless it was the last list element. */
-		if (qc->el_th_ctx.n != &th_ctx->quic_conns) {
-			struct quic_conn *next = LIST_NEXT(&qc->el_th_ctx,
-			                                   struct quic_conn *,
-			                                   el_th_ctx);
-			LIST_APPEND(&next->back_refs, &bref->users);
-		}
-		bref->ref = qc->el_th_ctx.n;
-		__ha_barrier_store();
-	}
-	/* Remove quic_conn from global ha_thread_ctx list. */
-	LIST_DELETE(&qc->el_th_ctx);
+	qc_detach_th_ctx_list(qc, 0);
 
 	pool_free(pool_head_quic_conn_rxbuf, qc->rx.buf.area);
 	pool_free(pool_head_quic_conn, qc);
@@ -8324,8 +8340,10 @@
 {
 	int thr;
 
-	for (thr = 0; thr < MAX_THREADS; ++thr)
+	for (thr = 0; thr < MAX_THREADS; ++thr) {
 		LIST_INIT(&ha_thread_ctx[thr].quic_conns);
+		LIST_INIT(&ha_thread_ctx[thr].quic_conns_clo);
+	}
 }
 INITCALL0(STG_INIT, init_quic);