]> git.kaiwu.me - haproxy.git/commitdiff
OPTIM: quic: rework the QUIC RX code
authorMaxime Henrion <mhenrion@haproxy.com>
Fri, 30 Jan 2026 16:39:04 +0000 (11:39 -0500)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 30 Apr 2026 13:33:07 +0000 (15:33 +0200)
Use an MPSC ring buffer to hold data for each datagram handler. Holding
this data in a per-handler buffer avoids the HoL blocking we experienced
when we had per-listener buffers with data from all threads mixed up
in them.

This also gets rid of the mt_list contention we were suffering before,
that was causing some threads to be stuck for a significant amount of
time, causing warnings and even crashes in some cases.

include/haproxy/proto_quic.h
include/haproxy/quic_rx-t.h
include/haproxy/quic_sock-t.h
include/haproxy/quic_sock.h
include/haproxy/receiver-t.h
src/proto_quic.c
src/quic_rx.c
src/quic_sock.c
src/quic_tp.c

index 1da8da8fe94caa5cfe6768ab5b7368d7e35db89c..58bc720b5336ac47f67d7c71ea2e12cb8d03f1bd 100644 (file)
@@ -25,6 +25,8 @@
 #include <haproxy/api-t.h>
 #include <haproxy/thread-t.h>
 
+#define QUIC_MAX_UDP_PAYLOAD_SIZE     2048
+
 extern struct protocol proto_quic4;
 extern struct protocol proto_quic6;
 
index 6b5a0c4c9f463fee50fdbfbf077e7ce9bed5d7a6..686e6fff131dc308f0a400d8ee471cdb15272bd4 100644 (file)
@@ -2,7 +2,6 @@
 #define _HAPROXY_RX_T_H
 
 extern struct pool_head *pool_head_quic_conn_rxbuf;
-extern struct pool_head *pool_head_quic_dgram;
 extern struct pool_head *pool_head_quic_rx_packet;
 
 #include <import/eb64tree.h>
index 69f52b45faf02f8806902342c23216dbd9d26196..eebafba52aee4fab79bcd472a1b1d47a9eabbd43 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <haproxy/buf-t.h>
 #include <haproxy/obj_type-t.h>
+#include <haproxy/mpring.h>
 
 /* QUIC socket allocation strategy. */
 enum quic_sock_mode {
@@ -17,15 +18,6 @@ struct quic_accept_queue {
        struct tasklet *tasklet;  /* task responsible to call listener_accept */
 };
 
-/* Buffer used to receive QUIC datagrams on random thread and redispatch them
- * to the connection thread.
- */
-struct quic_receiver_buf {
-       struct buffer buf; /* storage for datagrams received. */
-       struct list dgram_list; /* datagrams received with this rxbuf. */
-       struct mt_list rxbuf_el; /* list element into receiver.rxbuf_list. */
-};
-
 #define QUIC_DGRAM_FL_REJECT                   0x00000001
 #define QUIC_DGRAM_FL_SEND_RETRY               0x00000002
 
@@ -41,15 +33,12 @@ struct quic_dgram {
        struct sockaddr_storage daddr;
        struct quic_conn *qc;
 
-       struct list recv_list; /* element pointing to quic_receiver_buf <dgram_list>. */
-       struct mt_list handler_list; /* element pointing to quic_dghdlr <dgrams>. */
-
        int flags; /* QUIC_DGRAM_FL_* values */
 };
 
 /* QUIC datagram handler */
 struct quic_dghdlr {
-       struct mt_list dgrams;
+       struct mpring buf;      /* MPSC ring buffer for datagrams. */
        struct tasklet *task;
 };
 
index 26a2efdc1c202bb1ecb6b7a8773fc6cb3f02f255..e398d384a8357b4fe7603bea892b0aa389972c79 100644 (file)
@@ -45,6 +45,7 @@ struct connection *quic_sock_accept_conn(struct listener *l, int *status);
 
 struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state);
 void quic_lstnr_sock_fd_iocb(int fd);
+int quic_dgram_requeue(struct quic_dgram *dgram, int cid_tid);
 int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
                int flags, uint16_t gso_size);
 int qc_rcv_buf(struct quic_conn *qc);
index 976cc70c74993a33d443290d19ce6b1564e3a187..320e04b471f6a02472d1a96602ac663e565eddc3 100644 (file)
@@ -81,7 +81,6 @@ struct receiver {
        struct shard_info *shard_info;   /* points to info about the owning shard, NULL if single rx */
        struct list proto_list;          /* list in the protocol header */
 #ifdef USE_QUIC
-       struct mt_list rxbuf_list;       /* list of buffers to receive and dispatch QUIC datagrams. */
        enum quic_sock_mode quic_mode;   /* QUIC socket allocation strategy */
        unsigned int quic_curr_handshake; /* count of active QUIC handshakes */
        unsigned int quic_curr_accept;   /* count of QUIC conns waiting for accept */
index 50b231b2ac94b2dd5e2d57b6ed72c51b2412df69..fb606e32396bd505c1a44cb3491104b175bf3c55 100644 (file)
@@ -56,11 +56,9 @@ struct quic_dghdlr *quic_dghdlrs;
 static uint64_t quic_mem_global;
 THREAD_LOCAL struct cshared quic_mem_diff;
 
-/* Size of the internal buffer of QUIC RX buffer at the fd level */
+/* Size of the per-handler QUIC RX buffer at the fd level */
 #define QUIC_RX_BUFSZ  (1UL << 18)
 
-DECLARE_STATIC_POOL(pool_head_quic_rxbuf, "quic_rxbuf", QUIC_RX_BUFSZ);
-
 static int quic_bind_listener(struct listener *listener, char *errmsg, int errlen);
 static int quic_connect_server(struct connection *conn, int flags);
 static void quic_enable_listener(struct listener *listener);
@@ -442,44 +440,6 @@ int quic_connect_server(struct connection *conn, int flags)
        return SF_ERR_NONE;  /* connection is OK */
 }
 
-/* Allocate the RX buffers for <l> listener.
- * Return 1 if succeeded, 0 if not.
- */
-static int quic_alloc_rxbufs_listener(struct listener *l)
-{
-       int i;
-       struct quic_receiver_buf *tmp;
-
-       MT_LIST_INIT(&l->rx.rxbuf_list);
-       for (i = 0; i < my_popcountl(l->rx.bind_thread); i++) {
-               struct quic_receiver_buf *rxbuf;
-               char *buf;
-
-               rxbuf = calloc(1, sizeof(*rxbuf));
-               if (!rxbuf)
-                       goto err;
-
-               buf = pool_alloc(pool_head_quic_rxbuf);
-               if (!buf) {
-                       free(rxbuf);
-                       goto err;
-               }
-
-               rxbuf->buf = b_make(buf, QUIC_RX_BUFSZ, 0, 0);
-               LIST_INIT(&rxbuf->dgram_list);
-               MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
-       }
-
-       return 1;
-
- err:
-       while ((tmp = MT_LIST_POP(&l->rx.rxbuf_list, typeof(tmp), rxbuf_el))) {
-               pool_free(pool_head_quic_rxbuf, tmp->buf.area);
-               free(tmp);
-       }
-       return 0;
-}
-
 /* This function tries to bind a QUIC4/6 listener. It may return a warning or
  * an error message in <errmsg> if the message is at most <errlen> bytes long
  * (including '\0'). Note that <errmsg> may be NULL if <errlen> is also zero.
@@ -535,12 +495,6 @@ static int quic_bind_listener(struct listener *listener, char *errmsg, int errle
                break;
        }
 
-       if (!quic_alloc_rxbufs_listener(listener)) {
-               msg = "could not initialize tx/rx rings";
-               err |= ERR_WARN;
-               goto udp_return;
-       }
-
        if (global.tune.frontend_rcvbuf)
                setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.frontend_rcvbuf, sizeof(global.tune.frontend_rcvbuf));
 
@@ -632,6 +586,7 @@ REGISTER_PER_THREAD_INIT(quic_init_mem);
 
 static int quic_alloc_dghdlrs(void)
 {
+       char *buf;
        int i;
 
        quic_dghdlrs = calloc(global.nbthread, sizeof(*quic_dghdlrs));
@@ -643,6 +598,13 @@ static int quic_alloc_dghdlrs(void)
        for (i = 0; i < global.nbthread; i++) {
                struct quic_dghdlr *dghdlr = &quic_dghdlrs[i];
 
+               buf = malloc(QUIC_RX_BUFSZ);
+               if (!buf) {
+                       ha_alert("Failed to allocate the buffer for the quic datagram handler on thread %d.\n", i);
+                       return 0;
+               }
+               mpring_init(&dghdlr->buf, buf, QUIC_RX_BUFSZ);
+
                dghdlr->task = tasklet_new();
                if (!dghdlr->task) {
                        ha_alert("Failed to allocate the quic datagram handler on thread %d.\n", i);
@@ -652,8 +614,6 @@ static int quic_alloc_dghdlrs(void)
                tasklet_set_tid(dghdlr->task, i);
                dghdlr->task->context = dghdlr;
                dghdlr->task->process = quic_lstnr_dghdlr;
-
-               MT_LIST_INIT(&dghdlr->dgrams);
        }
 
        return 1;
index f4c39b40eb0abc43ea511ea09eef5faad00814ab..576ecdcc843ccf3f2be1d2b55a1c53c223a60c97 100644 (file)
@@ -37,7 +37,6 @@
 #include <haproxy/trace.h>
 
 DECLARE_POOL(pool_head_quic_conn_rxbuf, "quic_conn_rxbuf", QUIC_CONN_RX_BUFSZ);
-DECLARE_TYPED_POOL(pool_head_quic_dgram, "quic_dgram", struct quic_dgram);
 DECLARE_TYPED_POOL(pool_head_quic_rx_packet, "quic_rx_packet", struct quic_rx_packet);
 
 /* Decode an expected packet number from <truncated_on> its truncated value,
@@ -2462,9 +2461,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
                        if (!qc) {
                                if (new_tid >= 0) {
                                        TRACE_STATE("re-enqueue packet to conn thread", QUIC_EV_CONN_LPKT);
-                                       MT_LIST_APPEND(&quic_dghdlrs[new_tid].dgrams,
-                                                      &dgram->handler_list);
-                                       tasklet_wakeup(quic_dghdlrs[new_tid].task);
+                                       quic_dgram_requeue(dgram, new_tid);
                                        pool_free(pool_head_quic_rx_packet, pkt);
                                        goto out;
                                }
@@ -2523,16 +2520,12 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
        /* This must never happen. */
        BUG_ON(pos > end);
        BUG_ON(pos < end || pos > dgram->buf + dgram->len);
-       /* Mark this datagram as consumed */
-       HA_ATOMIC_STORE(&dgram->buf, NULL);
 
  out:
        TRACE_LEAVE(QUIC_EV_CONN_LPKT);
        return 0;
 
  err:
-       /* Mark this datagram as consumed as maybe at least some packets were parsed. */
-       HA_ATOMIC_STORE(&dgram->buf, NULL);
        TRACE_LEAVE(QUIC_EV_CONN_LPKT);
        return -1;
 }
index 38210b2a851b82a4588cf8db27cf396b4b8580be..72a07f1b7df6381539f918aaaf7a081c1c4bbbce 100644 (file)
@@ -192,30 +192,29 @@ struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state)
 {
        struct quic_dghdlr *dghdlr = ctx;
        struct quic_dgram *dgram;
+       size_t len;
        int max_dgrams = global.tune.maxpollevents;
 
        TRACE_ENTER(QUIC_EV_CONN_LPKT);
 
-       while ((dgram = MT_LIST_POP(&dghdlr->dgrams, typeof(dgram), handler_list))) {
-               if (quic_dgram_parse(dgram, NULL, dgram->owner)) {
-                       /* TODO should we requeue the datagram ? */
+       while ((dgram = mpring_read_begin(&dghdlr->buf, &len))) {
+               /* We ignore the return value of quic_dgram_parse() because
+                * whether it was successful or not, we still need to empty the
+                * ring buffer. Exiting early would leave us with data left to
+                * process, and no guarantee we would get woken up again.
+                */
+               quic_dgram_parse(dgram, NULL, dgram->owner);
+               mpring_read_end(&dghdlr->buf, len);
+
+               if (--max_dgrams <= 0) {
+                       /* too much work done at once, come back here later */
+                       tasklet_wakeup((struct tasklet *)t);
                        break;
                }
-
-               if (--max_dgrams <= 0)
-                       goto stop_here;
        }
 
        TRACE_LEAVE(QUIC_EV_CONN_LPKT);
        return t;
-
- stop_here:
-       /* too much work done at once, come back here later */
-       if (!MT_LIST_ISEMPTY(&dghdlr->dgrams))
-               tasklet_wakeup((struct tasklet *)t);
-
-       TRACE_LEAVE(QUIC_EV_CONN_LPKT);
-       return t;
 }
 
 /* Retrieve the DCID from a QUIC datagram or packet at <pos> position,
@@ -259,40 +258,13 @@ static int quic_get_dgram_dcid(unsigned char *pos, const unsigned char *end,
        goto leave;
 }
 
-
-/* Retrieve the DCID from the datagram found at <pos> position and deliver it to the
- * correct datagram handler.
- * Return 1 if a correct datagram could be found, 0 if not.
- */
-static int quic_lstnr_dgram_dispatch(unsigned char *pos, size_t len, void *owner,
-                                     struct sockaddr_storage *saddr,
-                                     struct sockaddr_storage *daddr,
-                                     struct quic_dgram *new_dgram, struct list *dgrams)
+/* Initialize a QUIC datagram. */
+static void quic_dgram_init(struct quic_dgram *dgram,
+                            unsigned char *pos, size_t len, void *owner,
+                            unsigned char *dcid, size_t dcid_len,
+                            struct sockaddr_storage *saddr,
+                            struct sockaddr_storage *daddr)
 {
-       struct quic_dgram *dgram;
-       unsigned char *dcid;
-       size_t dcid_len;
-       int cid_tid;
-
-       if (!len || !quic_get_dgram_dcid(pos, pos + len, &dcid, &dcid_len))
-               goto err;
-
-       dgram = new_dgram ? new_dgram : pool_alloc(pool_head_quic_dgram);
-       if (!dgram)
-               goto err;
-
-       if ((cid_tid = quic_get_cid_tid(dcid, dcid_len, saddr, pos, len)) < 0) {
-               /* Use the current thread if CID not found. If a clients opens
-                * a connection with multiple packets, it is possible that
-                * several threads will deal with datagrams sharing the same
-                * CID. For this reason, the CID tree insertion will be
-                * conducted as an atomic operation and the datagram ultimately
-                * redispatch by the late thread.
-                */
-               cid_tid = tid;
-       }
-
-       /* All the members must be initialized! */
        dgram->obj_type = OBJ_TYPE_DGRAM;
        dgram->owner = owner;
        dgram->buf = pos;
@@ -303,50 +275,6 @@ static int quic_lstnr_dgram_dispatch(unsigned char *pos, size_t len, void *owner
        dgram->daddr = *daddr;
        dgram->qc = NULL;
        dgram->flags = 0;
-
-       /* Attached datagram to its quic_receiver_buf and quic_dghdlrs. */
-       LIST_APPEND(dgrams, &dgram->recv_list);
-       MT_LIST_APPEND(&quic_dghdlrs[cid_tid].dgrams, &dgram->handler_list);
-
-       /* typically quic_lstnr_dghdlr() */
-       tasklet_wakeup(quic_dghdlrs[cid_tid].task);
-
-       return 1;
-
- err:
-       pool_free(pool_head_quic_dgram, new_dgram);
-       return 0;
-}
-
-/* This function is responsible to remove unused datagram attached in front of
- * <buf>. Each instances will be freed until a not yet consumed datagram is
- * found or end of the list is hit. The last unused datagram found is not freed
- * and is instead returned so that the caller can reuse it if needed.
- *
- * Returns the last unused datagram or NULL if no occurrence found.
- */
-static struct quic_dgram *quic_rxbuf_purge_dgrams(struct quic_receiver_buf *rbuf)
-{
-       struct quic_dgram *cur, *prev = NULL;
-
-       while (!LIST_ISEMPTY(&rbuf->dgram_list)) {
-               cur = LIST_ELEM(rbuf->dgram_list.n, struct quic_dgram *, recv_list);
-
-               /* Loop until a not yet consumed datagram is found. */
-               if (HA_ATOMIC_LOAD(&cur->buf))
-                       break;
-
-               /* Clear buffer of current unused datagram. */
-               LIST_DELETE(&cur->recv_list);
-               b_del(&rbuf->buf, cur->len);
-
-               /* Free last found unused datagram. */
-               pool_free(pool_head_quic_dgram, prev);
-               prev = cur;
-       }
-
-       /* Return last unused datagram found. */
-       return prev;
 }
 
 /* Receive a single message from datagram socket <fd>. Data are placed in <out>
@@ -463,105 +391,132 @@ static ssize_t quic_recv(int fd, void *out, size_t len,
        return ret;
 }
 
+/* Low-level function to write a datagram to the buffer of the handler thread. */
+static int quic_dgram_write(unsigned char *pos, size_t len, void *owner,
+                            unsigned char *dcid, size_t dcid_len,
+                            struct sockaddr_storage *saddr,
+                            struct sockaddr_storage *daddr,
+                            unsigned int cid_tid)
+{
+       struct mpring *dst;
+       struct quic_dgram *dgram;
+       unsigned char *data;
+       size_t bring_len;
+       void *buf;
+
+       dst = &quic_dghdlrs[cid_tid].buf;
+
+       bring_len = sizeof(struct quic_dgram) + len;
+       buf = mpring_write_reserve(dst, bring_len);
+       if (!buf)
+               return 0;
+
+       dgram = buf;
+       quic_dgram_init(dgram, pos, len, owner, dcid, dcid_len, saddr, daddr);
+
+       data = (unsigned char *)(dgram + 1);
+       memcpy(data, pos, len);
+
+       dgram->dcid = data + (dgram->dcid - dgram->buf);
+       dgram->buf = data;
+
+       mpring_write_commit(dst, buf, bring_len);
+
+       /* typically quic_lstnr_dghdlr() */
+       tasklet_wakeup(quic_dghdlrs[cid_tid].task);
+
+       return 1;
+}
+
+int quic_dgram_requeue(struct quic_dgram *dgram, int cid_tid)
+{
+
+       return quic_dgram_write(dgram->buf, dgram->len, dgram->owner,
+                               dgram->dcid, dgram->dcid_len,
+                               &dgram->saddr, &dgram->daddr, cid_tid);
+}
+
+/* Attempt to push a datagram to its handler thread.
+ *
+ * Returns 1 if successful, or 0 if the handler thread's buffer is full. If
+ * the datagram could not be pushed, it will be put on the appropriate pending
+ * lists. In all cases, take complete ownership of dgram - it won't be valid
+ * anymore after calling this function.
+ */
+static int quic_lstnr_dgram_dispatch(unsigned char *pos, size_t len, struct listener *l,
+                                     unsigned char *dcid, size_t dcid_len,
+                                     struct sockaddr_storage *saddr,
+                                     struct sockaddr_storage *daddr)
+{
+       struct proxy *px;
+       struct quic_counters *prx_counters;
+       int cid_tid;
+
+       if (!len)
+               goto err;
+
+       if (!dcid && !quic_get_dgram_dcid(pos, pos + len, &dcid, &dcid_len))
+               goto err;
+
+       if ((cid_tid = quic_get_cid_tid(dcid, dcid_len, saddr, pos, len)) < 0) {
+               /* Use the current thread if CID not found. If a clients opens
+                * a connection with multiple packets, it is possible that
+                * several threads will deal with datagrams sharing the same
+                * CID. For this reason, the CID tree insertion will be
+                * conducted as an atomic operation and the datagram ultimately
+                * redispatch by the late thread.
+                */
+               cid_tid = tid;
+       }
+
+       if (!quic_dgram_write(pos, len, l, dcid, dcid_len, saddr, daddr, cid_tid))
+               goto err;
+
+       return 1;
+
+ err:
+       px = l->bind_conf->frontend;
+       prx_counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, &quic_stats_module);
+       HA_ATOMIC_INC(&prx_counters->rxbuf_full);
+       return 0;
+}
+
 /* Function called on a read event from a listening socket. It tries
  * to handle as many connections as possible.
  */
 void quic_lstnr_sock_fd_iocb(int fd)
 {
        ssize_t ret;
-       struct quic_receiver_buf *rxbuf;
-       struct buffer *buf;
+       unsigned char buf[QUIC_MAX_UDP_PAYLOAD_SIZE];
        struct listener *l = objt_listener(fdtab[fd].owner);
        struct quic_transport_params *params;
        /* Source address */
        struct sockaddr_storage saddr = {0}, daddr = {0};
-       size_t max_sz, cspace;
-       struct quic_dgram *new_dgram;
-       unsigned char *dgram_buf;
+       size_t max_sz;
        int max_dgrams;
 
        BUG_ON(!l);
 
-       new_dgram = NULL;
-       if (!l)
-               return;
-
        if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
                return;
 
-       rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
-       if (!rxbuf)
-               goto out;
-
-       buf = &rxbuf->buf;
-
        max_dgrams = global.tune.maxpollevents;
- start:
-       /* Try to reuse an existing dgram. Note that there is always at
-        * least one datagram to pick, except the first time we enter
-        * this function for this <rxbuf> buffer.
-        */
-       new_dgram = quic_rxbuf_purge_dgrams(rxbuf);
-
        params = &l->bind_conf->quic_params;
        max_sz = params->max_udp_payload_size;
-       cspace = b_contig_space(buf);
-       if (cspace < max_sz) {
-               struct proxy *px = l->bind_conf->frontend;
-               struct quic_counters *prx_counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, &quic_stats_module);
-               struct quic_dgram *dgram;
-
-               /* Do no mark <buf> as full, and do not try to consume it
-                * if the contiguous remaining space is not at the end
-                */
-               if (b_tail(buf) + cspace < b_wrap(buf)) {
-                       HA_ATOMIC_INC(&prx_counters->rxbuf_full);
-                       goto out;
-               }
+       BUG_ON(max_sz > sizeof(buf));
 
-               /* Allocate a fake datagram, without data to locate
-                * the end of the RX buffer (required during purging).
-                */
-               dgram = pool_alloc(pool_head_quic_dgram);
-               if (!dgram)
-                       goto out;
-
-               /* Initialize only the useful members of this fake datagram. */
-               dgram->buf = NULL;
-               dgram->len = cspace;
-               /* Append this datagram only to the RX buffer list. It will
-                * not be treated by any datagram handler.
-                */
-               LIST_APPEND(&rxbuf->dgram_list, &dgram->recv_list);
-
-               /* Consume the remaining space */
-               b_add(buf, cspace);
-               if (b_contig_space(buf) < max_sz) {
-                       HA_ATOMIC_INC(&prx_counters->rxbuf_full);
-                       goto out;
-               }
-       }
-
-       dgram_buf = (unsigned char *)b_tail(buf);
-       ret = quic_recv(fd, dgram_buf, max_sz,
+ start:
+       ret = quic_recv(fd, buf, sizeof(buf),
                        (struct sockaddr *)&saddr, sizeof(saddr),
                        (struct sockaddr *)&daddr, sizeof(daddr),
                        get_net_port(&l->rx.addr), 1);
        if (ret <= 0)
-               goto out;
+               return;
+
+       quic_lstnr_dgram_dispatch(buf, ret, l, NULL, 0, &saddr, &daddr);
 
-       b_add(buf, ret);
-       if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr, &daddr,
-                                      new_dgram, &rxbuf->dgram_list)) {
-               /* If wrong, consume this datagram */
-               b_sub(buf, ret);
-       }
-       new_dgram = NULL;
        if (--max_dgrams > 0)
                goto start;
- out:
-       pool_free(pool_head_quic_dgram, new_dgram);
-       MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
 }
 
 /* FD-owned quic-conn socket callback. */
@@ -864,9 +819,10 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
 int qc_rcv_buf(struct quic_conn *qc)
 {
        struct sockaddr_storage saddr = {0}, daddr = {0};
-       struct quic_dgram *new_dgram = NULL;
+       struct quic_dgram dgram, *new_dgram;
        struct buffer buf = BUF_NULL;
-       unsigned char *dgram_buf;
+       unsigned char *dgram_buf, *dcid;
+       size_t dcid_len;
        ssize_t ret = 0;
        struct listener *l = qc->li;
 
@@ -875,6 +831,7 @@ int qc_rcv_buf(struct quic_conn *qc)
 
        TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
 
+       new_dgram = &dgram;
        do {
                if (!b_alloc(&buf, DB_MUX_RX))
                        break; /* TODO subscribe for memory again available. */
@@ -882,10 +839,6 @@ int qc_rcv_buf(struct quic_conn *qc)
                b_reset(&buf);
                BUG_ON(b_contig_space(&buf) < qc->max_udp_payload);
 
-               /* Allocate datagram on first loop or after requeuing. */
-               if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
-                       break; /* TODO subscribe for memory again available. */
-
                dgram_buf = (unsigned char *)b_tail(&buf);
                ret = quic_recv(qc->fd, dgram_buf, qc->max_udp_payload,
                                (struct sockaddr *)&saddr, sizeof(saddr),
@@ -901,90 +854,28 @@ int qc_rcv_buf(struct quic_conn *qc)
 
                b_add(&buf, ret);
 
-               new_dgram->obj_type = OBJ_TYPE_DGRAM;
-               new_dgram->buf = dgram_buf;
-               new_dgram->len = ret;
-               new_dgram->dcid_len = 0;
-               new_dgram->dcid = NULL;
-               new_dgram->saddr = saddr;
-               new_dgram->daddr = daddr;
-               new_dgram->qc = NULL;  /* set later via quic_dgram_parse() */
-               new_dgram->flags = 0;
-
                TRACE_DEVEL("read datagram", QUIC_EV_CONN_RCV, qc, new_dgram);
 
-               if (!quic_get_dgram_dcid(new_dgram->buf,
-                                        new_dgram->buf + new_dgram->len,
-                                        &new_dgram->dcid, &new_dgram->dcid_len)) {
+               if (!quic_get_dgram_dcid(dgram_buf, dgram_buf + ret, &dcid, &dcid_len))
                        continue;
-               }
 
-               if (l && !qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
+               if (l && !qc_check_dcid(qc, dcid, dcid_len)) {
                        /* Datagram received by error on the connection FD, dispatch it
                         * to its associated quic-conn.
                         *
                         * TODO count redispatch datagrams.
                         */
-                       struct quic_receiver_buf *rxbuf;
-                       struct quic_dgram *tmp_dgram;
-                       unsigned char *rxbuf_tail;
-                       size_t cspace;
-
                        TRACE_STATE("datagram for other connection on quic-conn socket, requeue it", QUIC_EV_CONN_RCV, qc);
 
-                       rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
-                       ASSUME_NONNULL(rxbuf);
-                       cspace = b_contig_space(&rxbuf->buf);
-
-                       tmp_dgram = quic_rxbuf_purge_dgrams(rxbuf);
-                       pool_free(pool_head_quic_dgram, tmp_dgram);
-
-                       /* Insert a fake datagram if space wraps to consume it. */
-                       if (cspace < new_dgram->len && b_space_wraps(&rxbuf->buf)) {
-                               struct quic_dgram *fake_dgram = pool_alloc(pool_head_quic_dgram);
-                               if (!fake_dgram) {
-                                       /* TODO count lost datagrams */
-                                       MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
-                                       continue;
-                               }
-
-                               fake_dgram->buf = NULL;
-                               fake_dgram->len = cspace;
-                               LIST_APPEND(&rxbuf->dgram_list, &fake_dgram->recv_list);
-                               b_add(&rxbuf->buf, cspace);
-                       }
-
-                       /* Recheck contig space after fake datagram insert. */
-                       if (b_contig_space(&rxbuf->buf) < new_dgram->len) {
-                               /* TODO count lost datagrams */
-                               MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
-                               continue;
-                       }
-
-                       rxbuf_tail = (unsigned char *)b_tail(&rxbuf->buf);
-                       __b_putblk(&rxbuf->buf, (char *)dgram_buf, new_dgram->len);
-                       if (!quic_lstnr_dgram_dispatch(rxbuf_tail, ret, l, &saddr, &daddr,
-                                                      new_dgram, &rxbuf->dgram_list)) {
-                               /* TODO count lost datagrams. */
-                               b_sub(&buf, ret);
-                       }
-                       else {
-                               /* datagram must not be freed as it was requeued. */
-                               new_dgram = NULL;
-                       }
-
-                       MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
+                       quic_lstnr_dgram_dispatch(dgram_buf, ret, l, dcid, dcid_len, &saddr, &daddr);
                        continue;
                }
 
+               quic_dgram_init(new_dgram, dgram_buf, ret, NULL, dcid, dcid_len, &saddr, &daddr);
                quic_dgram_parse(new_dgram, qc, l ? &l->obj_type :
                                 (qc->conn ? &__objt_server(qc->conn->target)->obj_type : NULL));
-               /* A datagram must always be consumed after quic_parse_dgram(). */
-               BUG_ON(new_dgram->buf);
        } while (ret > 0);
 
-       pool_free(pool_head_quic_dgram, new_dgram);
-
        if (b_size(&buf)) {
                b_free(&buf);
                offer_buffers(NULL, 1);
index ea27efcc7e3e59721f05da6e4e5d039b4ab1c072..5f75de30b330536e0785652e3395e2dfcc339261 100644 (file)
@@ -10,8 +10,6 @@
 #include <haproxy/quic_trace.h>
 #include <haproxy/trace.h>
 
-#define QUIC_MAX_UDP_PAYLOAD_SIZE     2048
-
 static int qc_early_tranport_params_validate(struct quic_conn *qc,
                                              struct quic_transport_params *p,
                                              struct quic_early_transport_params *e);