From 448d3d388abf29f5e6b2b453fc235d157a875240 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 28 May 2024 15:04:45 +0200 Subject: [PATCH] MINOR: quic: add GSO parameter on quic_sock send API Add parameter to qc_snd_buf(). When non-null, this specifies the value for socket option SOL_UDP/UDP_SEGMENT. This allows to send several datagrams in a single call by splitting data multiple times at boundary. For now, remains set to 0 by caller, as such there should not be any functional change. --- include/haproxy/quic_sock.h | 2 +- src/quic_conn.c | 2 +- src/quic_sock.c | 58 +++++++++++++++++++++++++++++++++---- src/quic_tx.c | 2 +- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h index 723614752..de7419021 100644 --- a/include/haproxy/quic_sock.h +++ b/include/haproxy/quic_sock.h @@ -44,7 +44,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 qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count, - int flags); + int flags, uint16_t gso_size); int qc_rcv_buf(struct quic_conn *qc); void quic_conn_sock_fd_iocb(int fd); diff --git a/src/quic_conn.c b/src/quic_conn.c index 680158cdb..f04ed727c 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -654,7 +654,7 @@ static struct task *quic_conn_closed_io_cb(struct task *t, void *context, unsign buf = b_make(cc_qc->cc_buf_area + headlen, QUIC_MAX_CC_BUFSIZE - headlen, 0, cc_qc->cc_dgram_len); - if (qc_snd_buf(qc, &buf, buf.data, 0) < 0) { + if (qc_snd_buf(qc, &buf, buf.data, 0, 0) < 0) { TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_IO_CB, qc); quic_release_cc_conn(cc_qc); cc_qc = NULL; diff --git a/src/quic_sock.c b/src/quic_sock.c index 06b65e083..b0153822d 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -652,8 +653,33 @@ static void cmsg_set_saddr(struct msghdr *msg, struct cmsghdr **cmsg, } } -/* Send a datagram stored into buffer with as size. - * The caller must ensure there is at least bytes in this buffer. +static void cmsg_set_gso(struct msghdr *msg, struct cmsghdr **cmsg, + uint16_t gso_size) +{ +#ifdef UDP_SEGMENT + struct cmsghdr *c; + size_t sz = sizeof(gso_size); + + /* Set first msg_controllen to be able to use CMSG_* macros. */ + msg->msg_controllen += CMSG_SPACE(sz); + + *cmsg = !(*cmsg) ? CMSG_FIRSTHDR(msg) : CMSG_NXTHDR(msg, *cmsg); + ALREADY_CHECKED(*cmsg); + c = *cmsg; + c->cmsg_len = CMSG_LEN(sz); + + c->cmsg_level = SOL_UDP; + c->cmsg_type = UDP_SEGMENT; + c->cmsg_len = CMSG_LEN(sz); + *((uint16_t *)CMSG_DATA(c)) = gso_size; +#endif +} + +/* Send a datagram stored into buffer with as size. The caller must + * ensure there is at least bytes in this buffer. + * + * If is non null, it will be used as value for UDP_SEGMENT option. + * This allows to transmit multiple datagrams in a single syscall. * * Returns the total bytes sent over the socket. 0 is returned if a transient * error is encountered which allows send to be retry later. A negative value @@ -664,7 +690,7 @@ static void cmsg_set_saddr(struct msghdr *msg, struct cmsghdr **cmsg, * done by removing the arg and replace it with address/port. */ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, - int flags) + int flags, uint16_t gso_size) { ssize_t ret; struct msghdr msg; @@ -673,15 +699,25 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, union { #ifdef IP_PKTINFO - char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + char buf[CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(gso_size))]; #endif /* IP_PKTINFO */ #ifdef IPV6_RECVPKTINFO - char buf6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + char buf6[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(gso_size))]; #endif /* IPV6_RECVPKTINFO */ - char bufaddr[CMSG_SPACE(sizeof(struct in_addr))]; + char bufaddr[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(gso_size))]; struct cmsghdr align; } ancillary_data; + /* man 3 cmsg + * + * When initializing a buffer that will contain a + * series of cmsghdr structures (e.g., to be sent with + * sendmsg(2)), that buffer should first be + * zero-initialized to ensure the correct operation of + * CMSG_NXTHDR(). + */ + memset(&ancillary_data, 0, sizeof(ancillary_data)); + vec.iov_base = b_peek(buf, b_head_ofs(buf)); vec.iov_len = sz; @@ -717,6 +753,16 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, cmsg_set_saddr(&msg, &cmsg, &qc->local_addr); } + /* Set GSO parameter if datagram size is bigger than MTU. */ + if (gso_size) { + /* GSO size must be less than total data to sent for multiple datagrams. */ + BUG_ON_HOT(b_data(buf) <= gso_size); + + if (!msg.msg_control) + msg.msg_control = ancillary_data.bufaddr; + cmsg_set_gso(&msg, &cmsg, gso_size); + } + do { ret = sendmsg(qc_fd(qc), &msg, MSG_DONTWAIT|MSG_NOSIGNAL); } while (ret < 0 && errno == EINTR); diff --git a/src/quic_tx.c b/src/quic_tx.c index bff49ac4c..3d8ae6815 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -300,7 +300,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) TRACE_PROTO("TX dgram", QUIC_EV_CONN_SPPKTS, qc); if (!skip_sendto) { - int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0); + int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, 0); if (ret < 0) { TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_SPPKTS, qc, first_pkt); qc_kill_conn(qc);