mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 07:37:02 +02:00
MEDIUM: quic: implement GSO fallback mechanism
UDP GSO on Linux is not implemented in every network devices. For example, this is not available for veth devices frequently used in container environment. In such case, EIO is reported on send() invocation. It is impossible to test at startup for proper GSO support in this case as a listener may be bound on multiple network interfaces. Furthermore, network interfaces may change during haproxy lifetime. As such, the only option is to react on send syscall error when GSO is used. The purpose of this patch is to implement a fallback when encountering such conditions. Emission can be retried immediately by trying to send each prepared datagrams individually. To support this, qc_send_ppkts() is able to iterate over each datagram in a so-called non-GSO fallback mode. Between each emission, a datagram header is rewritten in front of the buffer which allows the sending loop to proceed until last datagram is emitted. To complement this, quic_conn listener is flagged on first GSO send error with value LI_F_UDP_GSO_NOTSUPP. This completely disables GSO for all future emission with QUIC connections using this listener. For the moment, non-GSO fallback mode is activated when EIO is reported after GSO has been set. This is the error reported for the veth usage described above.
This commit is contained in:
parent
af22792a43
commit
d0ea173e35
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include <haproxy/quic_tx.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <haproxy/pool.h>
|
||||
#include <haproxy/trace.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user