]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: use quic-conn socket for reception
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 24 Oct 2022 15:40:37 +0000 (17:40 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 2 Dec 2022 13:45:43 +0000 (14:45 +0100)
Try to use the quic-conn socket for reception if it is allocated. For
this, the socket is inserted in the fdtab. This will call the new
handler quic_conn_io_cb() which is responsible to process the recv()
system call. It will reuse datagram dispatch for simplicity. However,
this is guaranteed to be called on the quic-conn thread, so it will be
more efficient to use a dedicated buffer. This will be implemented in
another commit.

This patch should improve performance by reducing contention on the
receiver socket. However, more gain can be obtained when the datagram
dispatch operation will be skipped.

Older quic_sock_fd_iocb() is renamed to quic_lstnr_sock_fd_iocb() to
emphasize its usage for the receiver socket.

This change is part of quic-conn owned socket implementation.
It may be backported to 2.7 after a period of observation.

include/haproxy/quic_conn-t.h
include/haproxy/quic_sock.h
src/proto_quic.c
src/quic_conn.c
src/quic_sock.c

index 1a4571d2bbfe83ea7c9f03484ce6c2da31280d29..44ae6a489437099653df762cc3878fb2ad302905 100644 (file)
@@ -221,6 +221,7 @@ enum quic_pkt_type {
 #define           QUIC_EV_CONN_IDLE_TIMER (1ULL << 45)
 #define           QUIC_EV_CONN_SUB       (1ULL << 46)
 #define           QUIC_EV_CONN_ELEVELSEL (1ULL << 47)
+#define           QUIC_EV_CONN_RCV       (1ULL << 48)
 
 /* Similar to kernel min()/max() definitions. */
 #define QUIC_MIN(a, b) ({ \
index 2788a230553f1437067db202f07f7aa7ec0ee39b..3bb87a25907a9a8d3614c36643d74dafbd4b5c7b 100644 (file)
@@ -42,7 +42,7 @@ int quic_sock_accepting_conn(const struct receiver *rx);
 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_sock_fd_iocb(int fd);
+void quic_lstnr_sock_fd_iocb(int fd);
 int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
                int flags);
 
index 9dd96337fe7b446830996f805e705c4df83dc5a3..57dafc3c227718524ce7c7234c8cdf5ef66dbcd1 100644 (file)
@@ -96,7 +96,7 @@ struct protocol proto_quic4 = {
        .rx_disable     = sock_disable,
        .rx_unbind      = sock_unbind,
        .rx_listening   = quic_sock_accepting_conn,
-       .default_iocb   = quic_sock_fd_iocb,
+       .default_iocb   = quic_lstnr_sock_fd_iocb,
        .receivers      = LIST_HEAD_INIT(proto_quic4.receivers),
        .nb_receivers   = 0,
 };
@@ -136,7 +136,7 @@ struct protocol proto_quic6 = {
        .rx_disable     = sock_disable,
        .rx_unbind      = sock_unbind,
        .rx_listening   = quic_sock_accepting_conn,
-       .default_iocb   = quic_sock_fd_iocb,
+       .default_iocb   = quic_lstnr_sock_fd_iocb,
        .receivers      = LIST_HEAD_INIT(proto_quic6.receivers),
        .nb_receivers   = 0,
 };
index 5c48b55d390ddbdcfd1c3d7d1cb37af810645998..65f4629ddb1aaa6ced92bf5b3dc02ca02bd50fc9 100644 (file)
@@ -165,6 +165,7 @@ static const struct trace_event quic_trace_events[] = {
        { .mask = QUIC_EV_TRANSP_PARAMS, .name = "transport_params", .desc = "transport parameters"},
        { .mask = QUIC_EV_CONN_IDLE_TIMER, .name = "idle_timer",     .desc = "idle timer task"},
        { .mask = QUIC_EV_CONN_SUB,      .name = "xprt_sub",         .desc = "RX/TX subcription or unsubscription to QUIC xprt"},
+       { .mask = QUIC_EV_CONN_RCV,      .name = "conn_recv",        .desc = "RX on connection" },
        { /* end */ }
 };
 
@@ -672,6 +673,13 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
                                chunk_appendf(&trace_buf, " next_level=%c", quic_enc_level_char(*next_level));
 
                }
+
+               if (mask & QUIC_EV_CONN_RCV) {
+                       const struct quic_dgram *dgram = a2;
+
+                       if (dgram)
+                               chunk_appendf(&trace_buf, " dgram.len=%zu", dgram->len);
+               }
        }
        if (mask & QUIC_EV_CONN_LPKT) {
                const struct quic_rx_packet *pkt = a2;
index 7de961392e0b944fada926ad9595ac1faad9cc6b..f1207b7502a724b5abc6fdd383c1d745434b50fd 100644 (file)
@@ -39,6 +39,9 @@
 #include <haproxy/task.h>
 #include <haproxy/trace.h>
 #include <haproxy/tools.h>
+#include <haproxy/trace.h>
+
+#define TRACE_SOURCE &trace_quic
 
 #define TRACE_SOURCE    &trace_quic
 
@@ -383,7 +386,7 @@ static ssize_t quic_recv(int fd, void *out, size_t len,
 /* Function called on a read event from a listening socket. It tries
  * to handle as many connections as possible.
  */
-void quic_sock_fd_iocb(int fd)
+void quic_lstnr_sock_fd_iocb(int fd)
 {
        ssize_t ret;
        struct quic_receiver_buf *rxbuf;
@@ -481,6 +484,89 @@ void quic_sock_fd_iocb(int fd)
        MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
 }
 
+/* FD-owned quic-conn socket callback. */
+static void quic_conn_sock_fd_iocb(int fd)
+{
+       struct quic_conn *qc;
+
+       struct sockaddr_storage saddr = {0}, daddr = {0};
+       struct quic_receiver_buf *rxbuf;
+       struct quic_transport_params *params;
+       struct quic_dgram *new_dgram;
+       struct buffer *buf;
+       size_t max_sz;
+       size_t cspace;
+       unsigned char *dgram_buf;
+       struct listener *l;
+       ssize_t ret = 0;
+
+       qc = fdtab[fd].owner;
+       l = qc->li;
+       TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
+
+       new_dgram = NULL;
+       rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
+       if (!rxbuf)
+               return;
+
+       buf = &rxbuf->buf;
+       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 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))
+                       goto end;
+
+               /* 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 end;
+
+               /* 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)
+                       goto end;
+       }
+
+       dgram_buf = (unsigned char *)b_tail(buf);
+       ret = quic_recv(qc->fd, dgram_buf, max_sz,
+                       (struct sockaddr *)&saddr, sizeof(saddr),
+                       (struct sockaddr *)&daddr, sizeof(daddr),
+                       get_net_port(&qc->local_addr));
+       if (ret <= 0)
+               goto end;
+
+       b_add(buf, ret);
+       if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &qc->peer_addr, &qc->local_addr,
+                                      new_dgram, &rxbuf->dgram_list)) {
+               b_del(buf, ret);
+       }
+       new_dgram = NULL;
+
+ end:
+       pool_free(pool_head_quic_dgram, new_dgram);
+       MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
+       TRACE_LEAVE(QUIC_EV_CONN_RCV, qc);
+       return;
+}
+
 /* Send a datagram stored into <buf> buffer with <sz> as size.
  * The caller must ensure there is at least <sz> bytes in this buffer.
  *
@@ -602,6 +688,8 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
 
        qc->fd = fd;
        fd_set_nonblock(fd);
+       fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
+       fd_want_recv(fd);
 
        return;
 
@@ -613,8 +701,10 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
 /* Release socket file-descriptor specific for QUIC connection <qc>. */
 void qc_release_fd(struct quic_conn *qc)
 {
-       if (qc_test_fd(qc))
+       if (qc_test_fd(qc)) {
+               fd_delete(qc->fd);
                qc->fd = DEAD_FD_MAGIC;
+       }
 }
 
 /*********************** QUIC accept queue management ***********************/