MINOR: quic: add GSO parameter on quic_sock send API

Add <gso_size> 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
<gso_size> boundary.

For now, <gso_size> remains set to 0 by caller, as such there should not
be any functional change.
This commit is contained in:
Amaury Denoyelle 2024-05-28 15:04:45 +02:00
parent 96a34d79d9
commit 448d3d388a
4 changed files with 55 additions and 9 deletions

View File

@ -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);

View File

@ -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;

View File

@ -16,6 +16,7 @@
#include <string.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <sys/socket.h>
#include <sys/types.h>
@ -652,8 +653,33 @@ static void cmsg_set_saddr(struct msghdr *msg, struct cmsghdr **cmsg,
}
}
/* Send a datagram stored into <buf> buffer with <sz> as size.
* The caller must ensure there is at least <sz> 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 <buf> buffer with <sz> as size. The caller must
* ensure there is at least <sz> bytes in this buffer.
*
* If <gso_size> 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 <qc> 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);

View File

@ -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);