]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: duplicate GSO unsupp status from listener to conn
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 6 Aug 2025 14:34:35 +0000 (16:34 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 7 Aug 2025 14:36:26 +0000 (16:36 +0200)
QUIC emission can use GSO to emit multiple datagrams with a single
syscall invokation. However, this feature relies on several kernel
parameters which are checked on haproxy process startup.

Even if these checks report no issue, GSO may still be unable due to the
underlying network adapter underneath. Thus, if a EIO occured on
sendmsg() with GSO, listener is flagged to mark GSO as unsupported. This
allows every other QUIC connections to share the status and avoid using
GSO when using this listener.

Previously, listener flag was checked for every QUIC emission. This was
done using an atomic operation to prevent races. Improve this by
duplicating GSO unsupported status as the connection level. This is done
on qc_new_conn() and also on thread rebinding if a new listener instance
is used.

The main benefit from this patch is to reduce the dependency between
quic_conn and listener instances.

include/haproxy/quic_conn-t.h
src/quic_conn.c
src/quic_tx.c

index 894fba723b22ea09394d8550b4bdce05582dc029..98434416de6f45d5c94ef095e72eb29cf47efd12 100644 (file)
@@ -450,7 +450,7 @@ struct quic_conn_closed {
 #define QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS    (1U << 2) /* HANDSHAKE_DONE must be sent */
 /* gap here */
 #define QUIC_FL_CONN_ACCEPT_REGISTERED           (1U << 4)
-/* gap here */
+#define QUIC_FL_CONN_UDP_GSO_EIO                 (1U << 5) /* GSO disabled due to a EIO occured on same listener */
 #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6)
 #define QUIC_FL_CONN_RETRANS_NEEDED              (1U << 7)
 #define QUIC_FL_CONN_RETRANS_OLD_DATA            (1U << 8) /* retransmission in progress for probing with already sent data */
@@ -489,6 +489,7 @@ static forceinline char *qc_show_flags(char *buf, size_t len, const char *delim,
        _(QUIC_FL_CONN_SPIN_BIT,
        _(QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS,
        _(QUIC_FL_CONN_ACCEPT_REGISTERED,
+       _(QUIC_FL_CONN_UDP_GSO_EIO,
        _(QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ,
        _(QUIC_FL_CONN_RETRANS_NEEDED,
        _(QUIC_FL_CONN_RETRANS_OLD_DATA,
@@ -507,7 +508,7 @@ static forceinline char *qc_show_flags(char *buf, size_t len, const char *delim,
        _(QUIC_FL_CONN_EXP_TIMER,
        _(QUIC_FL_CONN_CLOSING,
        _(QUIC_FL_CONN_DRAINING,
-       _(QUIC_FL_CONN_IMMEDIATE_CLOSE)))))))))))))))))))))));
+       _(QUIC_FL_CONN_IMMEDIATE_CLOSE))))))))))))))))))))))));
        /* epilogue */
        _(~0U);
        return buf;
index ff61c2029314ea010a6d5beb882ebbc77ec6850e..874f7ecb457269e4786def62cbbcf59bcc220a8d 100644 (file)
@@ -1178,6 +1178,11 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                cc_algo = l->bind_conf->quic_cc_algo;
 
                qc->flags = 0;
+
+               /* Duplicate GSO status on listener to connection */
+               if (HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP)
+                       qc->flags |= QUIC_FL_CONN_UDP_GSO_EIO;
+
                /* Mark this connection as having not received any token when 0-RTT is enabled. */
                if (l->bind_conf->ssl_conf.early_data && !token)
                        qc->flags |= QUIC_FL_CONN_NO_TOKEN_RCVD;
@@ -2013,9 +2018,16 @@ void qc_bind_tid_commit(struct quic_conn *qc, struct listener *new_li)
        /* At this point no connection was accounted for yet on this
         * listener so it's OK to just swap the pointer.
         */
-       if (new_li && new_li != __objt_listener(qc->target))
+       if (new_li && new_li != __objt_listener(qc->target)) {
                qc->target = &new_li->obj_type;
 
+               /* Update GSO conn support based on new listener status. */
+               if (HA_ATOMIC_LOAD(&new_li->flags) & LI_F_UDP_GSO_NOTSUPP)
+                       qc->flags |= QUIC_FL_CONN_UDP_GSO_EIO;
+               else
+                       qc->flags &= ~QUIC_FL_CONN_UDP_GSO_EIO;
+       }
+
        /* Rebind the connection FD. */
        if (qc_test_fd(qc)) {
                /* Reading is reactivated by the new thread. */
index 27420260d723be9d58132a82c23ce6b9caf29ea7..d5f00168bf2ea6d4f2badcf1adb3af5809cad348 100644 (file)
@@ -307,11 +307,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
 
                /* If datagram bigger than MTU, several ones were encoded for GSO usage. */
                if (dglen > qc->path->mtu) {
-                       /* TODO: note that at this time for connection to backends this
-                        * part is not run because no more than an MTU has been prepared for
-                        * such connections (dglen <= qc->path->mtu). So, here l is not NULL.
-                        */
-                       if (likely(!(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP))) {
+                       if (likely(!(qc->flags & QUIC_FL_CONN_UDP_GSO_EIO))) {
                                TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
                                gso = qc->path->mtu;
                        }
@@ -333,6 +329,9 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
                        int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
                        if (ret < 0) {
                                if (gso && ret == -EIO) {
+                                       /* GSO must not be used if already disabled. */
+                                       BUG_ON(qc->flags & QUIC_FL_CONN_UDP_GSO_EIO);
+
                                        /* TODO: note that at this time for connection to backends this
                                         * part is not run because no more than an MTU has been
                                         * prepared for such connections (l is not NULL).
@@ -342,6 +341,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
                                         */
                                        TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
                                        HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP);
+                                       qc->flags |= QUIC_FL_CONN_UDP_GSO_EIO;
                                        continue;
                                }
 
@@ -788,10 +788,10 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                                prv_pkt = cur_pkt;
                        }
                        else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
+                                !(qc->flags & QUIC_FL_CONN_UDP_GSO_EIO) &&
                                 dglen == qc->path->mtu &&
                                 (char *)end < b_wrap(buf) &&
-                                ++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS &&
-                                l && !(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP)) {
+                                ++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS) {
                                /* TODO: note that for backends GSO is not used. No more than
                                 * an MTU is prepared.
                                 */