return 0;
}
-/* Release all the memory allocated for <conn> QUIC connection. */
-static void quic_conn_free(struct quic_conn *qc)
+/* Increment the <qc> refcount.
+ *
+ * This operation must be conducted when manipulating the quic_conn outside of
+ * the connection pinned thread. These threads can only retrieve the connection
+ * in the CID tree, so this function must be conducted under the CID lock.
+ */
+static inline void quic_conn_take(struct quic_conn *qc)
+{
+ HA_ATOMIC_INC(&qc->refcount);
+}
+
+/* Decrement the <qc> refcount. If the refcount is zero *BEFORE* the
+ * substraction, the quic_conn is freed.
+ */
+static void quic_conn_drop(struct quic_conn *qc)
{
+ struct ssl_sock_ctx *conn_ctx;
int i;
if (!qc)
return;
- free_quic_conn_cids(qc);
+ HA_RWLOCK_WRLOCK(QUIC_LOCK, &qc->li->rx.cids_lock);
+ if (HA_ATOMIC_FETCH_SUB(&qc->refcount, 1)) {
+ HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &qc->li->rx.cids_lock);
+ return;
+ }
/* remove the connection from receiver cids trees */
- HA_RWLOCK_WRLOCK(QUIC_LOCK, &qc->li->rx.cids_lock);
ebmb_delete(&qc->odcid_node);
ebmb_delete(&qc->scid_node);
-
+ free_quic_conn_cids(qc);
HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &qc->li->rx.cids_lock);
+ conn_ctx = HA_ATOMIC_LOAD(&qc->xprt_ctx);
+ if (conn_ctx) {
+ tasklet_free(conn_ctx->wait_event.tasklet);
+ conn_ctx->wait_event.tasklet = NULL;
+
+ pool_free(pool_head_quic_conn_ctx, conn_ctx);
+ }
+
for (i = 0; i < QUIC_TLS_ENC_LEVEL_MAX; i++)
quic_conn_enc_level_uninit(&qc->els[i]);
qc->timer_task = NULL;
}
- quic_conn_free(qc);
+ quic_conn_drop(qc);
}
/* Callback called upon loss detection and PTO timer expirations. */
goto err;
}
+ HA_ATOMIC_STORE(&qc->refcount, 0);
+
buf_area = pool_alloc(pool_head_quic_conn_rxbuf);
if (!buf_area) {
TRACE_PROTO("Could not allocate a new RX buffer", QUIC_EV_CONN_INIT, qc);
err:
TRACE_DEVEL("leaving in error", QUIC_EV_CONN_INIT, qc ? qc : NULL);
- quic_conn_free(qc);
+ quic_conn_drop(qc);
return NULL;
}
found_in_dcid = 1;
end:
+ if (qc)
+ quic_conn_take(qc);
HA_RWLOCK_RDUNLOCK(QUIC_LOCK, &l->rx.cids_lock);
/* If found in DCIDs tree, remove the quic_conn from the ODCIDs tree.
struct sockaddr_storage *saddr)
{
unsigned char *beg, *payload;
- struct quic_conn *qc;
+ struct quic_conn *qc, *qc_to_purge = NULL;
struct listener *l;
struct ssl_sock_ctx *conn_ctx;
int long_header = 0;
/* Insert the DCID the QUIC client has chosen (only for listeners) */
n = ebmb_insert(&l->rx.odcids, &qc->odcid_node,
qc->odcid.len + qc->odcid.addrlen);
- HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &l->rx.cids_lock);
/* If the insertion failed, it means that another
* thread has already allocated a QUIC connection for
* the same CID. Liberate our allocated connection.
*/
if (unlikely(n != &qc->odcid_node)) {
- quic_conn_free(qc);
+ qc_to_purge = qc;
+
qc = ebmb_entry(n, struct quic_conn, odcid_node);
pkt->qc = qc;
}
- else {
+
+ quic_conn_take(qc);
+ HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &l->rx.cids_lock);
+
+ if (likely(!qc_to_purge)) {
/* Enqueue this packet. */
pkt->qc = qc;
MT_LIST_APPEND(&l->rx.pkts, &pkt->rx_list);
/* Try to accept a new connection. */
listener_accept(l);
}
+ else {
+ quic_conn_drop(qc_to_purge);
+ }
}
else {
pkt->qc = qc;
TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc ? qc : NULL, pkt);
+ if (qc)
+ quic_conn_drop(qc);
+
return pkt->len;
err:
pkt->len = end - beg;
TRACE_DEVEL("Leaving in error", QUIC_EV_CONN_LPKT,
qc ? qc : NULL, pkt);
+
+ if (qc)
+ quic_conn_drop(qc);
+
return -1;
}