diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h index b9a8447cb..becc83b01 100644 --- a/include/haproxy/listener-t.h +++ b/include/haproxy/listener-t.h @@ -264,6 +264,7 @@ struct listener { /* listener flags (16 bits) */ #define LI_F_FINALIZED 0x0001 /* listener made it to the READY||LIMITED||FULL state at least once, may be suspended/resumed safely */ #define LI_F_SUSPENDED 0x0002 /* listener has been suspended using suspend_listener(), it is either is LI_PAUSED or LI_ASSIGNED state */ +#define LI_F_UDP_GSO_NOTSUPP 0x0004 /* UDP GSO disabled after send error */ /* Descriptor for a "bind" keyword. The ->parse() function returns 0 in case of * success, or a combination of ERR_* flags if an error is encountered. The diff --git a/src/quic_sock.c b/src/quic_sock.c index b0153822d..541bd6cbb 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -789,7 +789,7 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, qc->cntrs.sendto_err_unknown++; TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0, "UDP send failure errno=%d (%s)", errno, strerror(errno)); - return -1; + return -errno; } } diff --git a/src/quic_tx.c b/src/quic_tx.c index ad912eb2a..06c973a45 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -14,6 +14,8 @@ #include +#include + #include #include #include @@ -288,7 +290,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) unsigned char *pos; struct buffer tmpbuf = { }; struct quic_tx_packet *first_pkt, *pkt, *next_pkt; - uint16_t dglen, gso = 0; + uint16_t dglen, gso = 0, gso_fallback = 0; unsigned int time_sent; pos = (unsigned char *)b_head(buf); @@ -297,8 +299,16 @@ 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) { - TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc); - gso = qc->path->mtu; + if (likely(!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP))) { + TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc); + gso = qc->path->mtu; + } + else { + TRACE_PROTO("use non-GSO fallback emission mode", QUIC_EV_CONN_SPPKTS, qc); + gso_fallback = dglen; + /* Only send a single datagram now that GSO is disabled. */ + dglen = qc->path->mtu; + } } first_pkt = read_ptr(pos + sizeof(dglen)); @@ -310,6 +320,15 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) if (!skip_sendto) { int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso); if (ret < 0) { + if (gso && ret == -EIO) { + /* Disable permanently UDP GSO for this listener. + * Retry standard emission. + */ + TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt); + HA_ATOMIC_OR(&qc->li->flags, LI_F_UDP_GSO_NOTSUPP); + continue; + } + TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_SPPKTS, qc, first_pkt); qc_kill_conn(qc); qc_free_tx_coalesced_pkts(qc, first_pkt); @@ -336,6 +355,31 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) for (pkt = first_pkt; pkt; pkt = next_pkt) { struct quic_cc *cc = &qc->path->cc; + + /* Packets built with GSO from consecutive datagrams + * are attached together but without COALESCED flag. + * Unlink them to treat them separately on ACK Rx. + */ + if (!(pkt->flags & QUIC_FL_TX_PACKET_COALESCED)) { + if (pkt->prev) { + pkt->prev->next = NULL; + pkt->prev = NULL; + } + + /* Packet from first dgram only were sent on non-GSO fallback. */ + if (gso_fallback) { + BUG_ON_HOT(gso_fallback < dglen); + gso_fallback -= dglen; + + /* Built a new datagram header. */ + buf->head -= QUIC_DGRAM_HEADLEN; + b_add(buf, QUIC_DGRAM_HEADLEN); + write_u16(b_head(buf), gso_fallback); + write_ptr(b_head(buf) + sizeof(gso_fallback), pkt); + break; + } + } + qc->cntrs.sent_pkt++; pkt->time_sent = time_sent; @@ -375,17 +419,6 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) next_pkt = pkt->next; quic_tx_packet_refinc(pkt); eb64_insert(&pkt->pktns->tx.pkts, &pkt->pn_node); - - /* Packets built with GSO from consecutive datagrams - * are attached together but without COALESCED flag. - * Unlink them to treat them separately on ACK Rx. - */ - if (!(pkt->flags & QUIC_FL_TX_PACKET_COALESCED)) { - if (pkt->prev) { - pkt->prev->next = NULL; - pkt->prev = NULL; - } - } } } @@ -665,6 +698,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf, prv_pkt = cur_pkt; } else if (!(global.tune.options & GTUNE_QUIC_NO_UDP_GSO) && + !(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP) && dglen == qc->path->mtu && (char *)end < b_wrap(buf) && gso_dgram_cnt < 64) {