mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Merge 6b5bc6d41c
into 1163af9c41
This commit is contained in:
commit
9aba85cf66
4 changed files with 207 additions and 22 deletions
35
bin/prog.c
35
bin/prog.c
|
@ -171,6 +171,8 @@ prog_print_common_options (const struct prog *prog, FILE *out)
|
||||||
" sndbuf=12345 # Sets SO_SNDBUF\n"
|
" sndbuf=12345 # Sets SO_SNDBUF\n"
|
||||||
" rcvbuf=12345 # Sets SO_RCVBUF\n"
|
" rcvbuf=12345 # Sets SO_RCVBUF\n"
|
||||||
" -W Use stock PMI (malloc & free)\n"
|
" -W Use stock PMI (malloc & free)\n"
|
||||||
|
" -O BURST Use UDP GSO (if available). BURST factor is the max packets\n"
|
||||||
|
" that can be aggregated in single sendmsg.\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
#if HAVE_SENDMMSG
|
#if HAVE_SENDMMSG
|
||||||
|
@ -226,6 +228,34 @@ prog_print_common_options (const struct prog *prog, FILE *out)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_GSO
|
||||||
|
/* Test at runtime if the GSO support is available. setsockopt(UDP_SEGMENT)
|
||||||
|
* should be successful if the GSO is supported.
|
||||||
|
* Returns non-zero if GSO supported. */
|
||||||
|
int supports_gso(void)
|
||||||
|
{
|
||||||
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
int gso_size = 1400; // just for test
|
||||||
|
|
||||||
|
if(fd < 0) {
|
||||||
|
LSQ_ERROR("weird! socket failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (setsockopt(fd, SOL_UDP, UDP_SEGMENT, &gso_size, sizeof(gso_size))) {
|
||||||
|
LSQ_INFO("gso setsockopt failed. GSO not supp");
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
LSQ_INFO("GSO is supported");
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int supports_gso(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // HAVE_GSO
|
||||||
|
|
||||||
int
|
int
|
||||||
prog_set_opt (struct prog *prog, int opt, const char *arg)
|
prog_set_opt (struct prog *prog, int opt, const char *arg)
|
||||||
|
@ -237,6 +267,11 @@ prog_set_opt (struct prog *prog, int opt, const char *arg)
|
||||||
|
|
||||||
switch (opt)
|
switch (opt)
|
||||||
{
|
{
|
||||||
|
case 'O':
|
||||||
|
if(supports_gso()) {
|
||||||
|
prog->prog_gso_burst = (unsigned)atoi(arg);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
#if LSQUIC_DONTFRAG_SUPPORTED
|
#if LSQUIC_DONTFRAG_SUPPORTED
|
||||||
case 'D':
|
case 'D':
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct prog
|
||||||
unsigned short prog_max_packet_size;
|
unsigned short prog_max_packet_size;
|
||||||
int prog_version_cleared;
|
int prog_version_cleared;
|
||||||
unsigned long prog_read_count;
|
unsigned long prog_read_count;
|
||||||
|
unsigned prog_gso_burst;
|
||||||
#if HAVE_SENDMMSG
|
#if HAVE_SENDMMSG
|
||||||
int prog_use_sendmmsg;
|
int prog_use_sendmmsg;
|
||||||
#endif
|
#endif
|
||||||
|
@ -74,7 +75,7 @@ prog_init (struct prog *, unsigned lsquic_engine_flags, struct sport_head *,
|
||||||
# define IP_DONTFRAG_FLAG ""
|
# define IP_DONTFRAG_FLAG ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PROG_OPTS "i:km:c:y:L:l:o:H:s:S:Y:z:G:W" RECVMMSG_FLAG SENDMMSG_FLAG \
|
#define PROG_OPTS "O:i:km:c:y:L:l:o:H:s:S:Y:z:G:W" RECVMMSG_FLAG SENDMMSG_FLAG \
|
||||||
IP_DONTFRAG_FLAG
|
IP_DONTFRAG_FLAG
|
||||||
|
|
||||||
/* Returns:
|
/* Returns:
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -1304,6 +1305,7 @@ enum ctl_what
|
||||||
#if ECN_SUPPORTED
|
#if ECN_SUPPORTED
|
||||||
CW_ECN = 1 << 1,
|
CW_ECN = 1 << 1,
|
||||||
#endif
|
#endif
|
||||||
|
CW_PKTLEN = 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1313,7 +1315,7 @@ setup_control_msg (
|
||||||
#else
|
#else
|
||||||
WSAMSG
|
WSAMSG
|
||||||
#endif
|
#endif
|
||||||
*msg, enum ctl_what cw,
|
*msg, enum ctl_what cw, uint16_t pktlen,
|
||||||
const struct lsquic_out_spec *spec, unsigned char *buf, size_t bufsz)
|
const struct lsquic_out_spec *spec, unsigned char *buf, size_t bufsz)
|
||||||
{
|
{
|
||||||
struct cmsghdr *cmsg;
|
struct cmsghdr *cmsg;
|
||||||
|
@ -1417,6 +1419,17 @@ setup_control_msg (
|
||||||
}
|
}
|
||||||
cw &= ~CW_ECN;
|
cw &= ~CW_ECN;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if HAVE_GSO
|
||||||
|
else if (cw & CW_PKTLEN)
|
||||||
|
{
|
||||||
|
cmsg->cmsg_level = SOL_UDP;
|
||||||
|
cmsg->cmsg_type = UDP_SEGMENT;
|
||||||
|
cmsg->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
||||||
|
ctl_len += CMSG_SPACE(sizeof(uint16_t));
|
||||||
|
*(uint16_t *)CMSG_DATA(cmsg) = pktlen;
|
||||||
|
cw &= ~CW_PKTLEN;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
assert(0);
|
assert(0);
|
||||||
|
@ -1429,6 +1442,18 @@ setup_control_msg (
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
void check_if_single_peer(const struct lsquic_out_spec *specs,
|
||||||
|
unsigned count)
|
||||||
|
{
|
||||||
|
void *ctx;
|
||||||
|
unsigned i;
|
||||||
|
for (i = 1, ctx = specs[i].peer_ctx;
|
||||||
|
i < count;
|
||||||
|
ctx = specs[i].peer_ctx, ++i)
|
||||||
|
assert(ctx == specs[i - 1].peer_ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if HAVE_SENDMMSG
|
#if HAVE_SENDMMSG
|
||||||
static int
|
static int
|
||||||
|
@ -1436,18 +1461,11 @@ send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
|
||||||
unsigned count)
|
unsigned count)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
{
|
/* This only works for a single port! If the specs contain more
|
||||||
/* This only works for a single port! If the specs contain more
|
* than one socket, this function does *NOT* work. We check it
|
||||||
* than one socket, this function does *NOT* work. We check it
|
* here just in case:
|
||||||
* here just in case:
|
*/
|
||||||
*/
|
check_if_single_peer(specs, count);
|
||||||
void *ctx;
|
|
||||||
unsigned i;
|
|
||||||
for (i = 1, ctx = specs[i].peer_ctx;
|
|
||||||
i < count;
|
|
||||||
ctx = specs[i].peer_ctx, ++i)
|
|
||||||
assert(ctx == specs[i - 1].peer_ctx);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const struct service_port *const sport = specs[0].peer_ctx;
|
const struct service_port *const sport = specs[0].peer_ctx;
|
||||||
|
@ -1518,7 +1536,7 @@ send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
|
||||||
else if (cw)
|
else if (cw)
|
||||||
{
|
{
|
||||||
prev_ancil_key = ancil_key;
|
prev_ancil_key = ancil_key;
|
||||||
setup_control_msg(&mmsgs[i].msg_hdr, cw, &specs[i], ancil[i].buf,
|
setup_control_msg(&mmsgs[i].msg_hdr, cw, 0, &specs[i], ancil[i].buf,
|
||||||
sizeof(ancil[i].buf));
|
sizeof(ancil[i].buf));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1584,9 +1602,13 @@ find_sport (struct prog *prog, const struct sockaddr *local_sa)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Non-zero pktlen indicates use of UDP GSO. pktlen is used to create gso_size
|
||||||
|
* CMSG needed for UDP GSO.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
|
send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count,
|
||||||
|
uint16_t pktlen)
|
||||||
{
|
{
|
||||||
const struct service_port *sport;
|
const struct service_port *sport;
|
||||||
enum ctl_what cw;
|
enum ctl_what cw;
|
||||||
|
@ -1611,6 +1633,7 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
|
||||||
#if ECN_SUPPORTED
|
#if ECN_SUPPORTED
|
||||||
+ CMSG_SPACE(sizeof(int))
|
+ CMSG_SPACE(sizeof(int))
|
||||||
#endif
|
#endif
|
||||||
|
+ CMSG_SPACE(sizeof(uint16_t))
|
||||||
];
|
];
|
||||||
struct cmsghdr cmsg;
|
struct cmsghdr cmsg;
|
||||||
} ancil;
|
} ancil;
|
||||||
|
@ -1699,6 +1722,10 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
|
||||||
ancil_key |= specs[n].ecn;
|
ancil_key |= specs[n].ecn;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (pktlen)
|
||||||
|
{
|
||||||
|
cw |= CW_PKTLEN;
|
||||||
|
}
|
||||||
if (cw && prev_ancil_key == ancil_key)
|
if (cw && prev_ancil_key == ancil_key)
|
||||||
{
|
{
|
||||||
/* Reuse previous ancillary message */
|
/* Reuse previous ancillary message */
|
||||||
|
@ -1707,7 +1734,7 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
|
||||||
else if (cw)
|
else if (cw)
|
||||||
{
|
{
|
||||||
prev_ancil_key = ancil_key;
|
prev_ancil_key = ancil_key;
|
||||||
setup_control_msg(&msg, cw, &specs[n], ancil.buf, sizeof(ancil.buf));
|
setup_control_msg(&msg, cw, pktlen, &specs[n], ancil.buf, sizeof(ancil.buf));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1764,18 +1791,125 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_GSO
|
||||||
|
/* UDP GSO
|
||||||
|
* Refs:
|
||||||
|
* https://lwn.net/Articles/752956/
|
||||||
|
* http://vger.kernel.org/lpc_net2018_talks/willemdebruijn-lpc2018-udpgso-paper-DRAFT-1.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int send_iovecs_gso(struct lsquic_out_spec *spec,
|
||||||
|
struct iovec *vecs, unsigned vcnt)
|
||||||
|
{
|
||||||
|
spec->iov = vecs;
|
||||||
|
spec->iovlen = vcnt;
|
||||||
|
LSQ_DEBUG("GSO with burst:%d", vcnt);
|
||||||
|
return send_packets_one_by_one (spec, 1, vcnt > 1? vecs[0].iov_len: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return true if specs match */
|
||||||
|
int match_spec(const struct lsquic_out_spec *s1,
|
||||||
|
const struct lsquic_out_spec *s2)
|
||||||
|
{
|
||||||
|
if (s1->local_sa != s2->local_sa ||
|
||||||
|
s1->dest_sa != s2->dest_sa ||
|
||||||
|
s1->peer_ctx != s2->peer_ctx ||
|
||||||
|
s1->ecn != s2->ecn)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To use GSO the flow here is:
|
||||||
|
* a. Check all the equal length iovs and batch them in single iovec array and
|
||||||
|
* pass it in spec.
|
||||||
|
* b. Batching inter-spec iovs can happen only if all the spec parameters
|
||||||
|
* (peer_ctx, sa, ecn) match.
|
||||||
|
* c. The last packet in the iovec array can be of smaller length then all the
|
||||||
|
* earlier packets.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
send_packets_using_gso (const unsigned burst,
|
||||||
|
const struct lsquic_out_spec *specs,
|
||||||
|
unsigned count)
|
||||||
|
{
|
||||||
|
struct iovec vecs[burst];
|
||||||
|
struct iovec *inv;
|
||||||
|
struct lsquic_out_spec newspec;
|
||||||
|
unsigned i, j, vcnt = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (count == 0 || specs[0].iovlen == 0) {
|
||||||
|
LSQ_ERROR("Sanity failed. count=%d, specs iovlen=%zu",
|
||||||
|
count, specs[0].iovlen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Coalesce packets with same lengths, except that the last packet can be
|
||||||
|
* of smaller length. Colasece max gso_burst packets. */
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
if (vcnt == 0) {
|
||||||
|
memcpy(&newspec, &specs[i], sizeof(struct lsquic_out_spec));
|
||||||
|
} else if(!match_spec(&newspec, &specs[i])) {
|
||||||
|
// new specs dont match prev ones, so send the previous iovec batch
|
||||||
|
ret = send_iovecs_gso (&newspec, vecs, vcnt);
|
||||||
|
if(ret == 0) {
|
||||||
|
LSQ_ERROR("Partial send1");
|
||||||
|
return i-1;
|
||||||
|
}
|
||||||
|
vcnt = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (j = 0; j < specs[i].iovlen; ++j)
|
||||||
|
{
|
||||||
|
inv = &specs[i].iov[j];
|
||||||
|
if (vcnt == 0) {
|
||||||
|
vecs[vcnt++] = *inv;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inv->iov_len > vecs[0].iov_len) {
|
||||||
|
ret = send_iovecs_gso (&newspec, vecs, vcnt);
|
||||||
|
vcnt = 0;
|
||||||
|
} else if ((inv->iov_len != vecs[0].iov_len) || (vcnt >= (burst-1))) {
|
||||||
|
vecs[vcnt++] = *inv;
|
||||||
|
ret = send_iovecs_gso (&newspec, vecs, vcnt);
|
||||||
|
vcnt = 0;
|
||||||
|
} else {
|
||||||
|
vecs[vcnt++] = *inv;
|
||||||
|
}
|
||||||
|
if(vcnt == 0 && ret == 0) {
|
||||||
|
LSQ_ERROR("Partial send2");
|
||||||
|
return i-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vcnt) {
|
||||||
|
ret = send_iovecs_gso (&newspec, vecs, vcnt);
|
||||||
|
if(ret == 0) {
|
||||||
|
LSQ_ERROR("Partial send3");
|
||||||
|
return count-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
#endif // HAVE_GSO
|
||||||
|
|
||||||
int
|
int
|
||||||
sport_packets_out (void *ctx, const struct lsquic_out_spec *specs,
|
sport_packets_out (void *ctx, const struct lsquic_out_spec *specs,
|
||||||
unsigned count)
|
unsigned count)
|
||||||
{
|
{
|
||||||
#if HAVE_SENDMMSG
|
|
||||||
const struct prog *prog = ctx;
|
const struct prog *prog = ctx;
|
||||||
if (prog->prog_use_sendmmsg)
|
#if HAVE_GSO
|
||||||
return send_packets_using_sendmmsg(specs, count);
|
if (prog->prog_gso_burst > 0)
|
||||||
|
return send_packets_using_gso(prog->prog_gso_burst, specs, count);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
return send_packets_one_by_one(specs, count);
|
#if HAVE_SENDMMSG
|
||||||
|
if (prog->prog_use_sendmmsg)
|
||||||
|
return send_packets_using_sendmmsg(specs, count);
|
||||||
|
#endif
|
||||||
|
return send_packets_one_by_one(specs, count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,21 @@ destroy_lsquic_reader_ctx (struct reader_ctx *ctx);
|
||||||
#define LITESPEED_ID "lsquic" "/" TOSTRING(LSQUIC_MAJOR_VERSION) "." \
|
#define LITESPEED_ID "lsquic" "/" TOSTRING(LSQUIC_MAJOR_VERSION) "." \
|
||||||
TOSTRING(LSQUIC_MINOR_VERSION) "." TOSTRING(LSQUIC_PATCH_VERSION)
|
TOSTRING(LSQUIC_MINOR_VERSION) "." TOSTRING(LSQUIC_PATCH_VERSION)
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#ifndef UDP_SEGMENT
|
||||||
|
#define UDP_SEGMENT 103
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __linux__
|
||||||
|
#ifndef HAVE_GSO
|
||||||
|
#if defined(SOL_UDP) && defined(UDP_SEGMENT)
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#define HAVE_GSO 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
struct header_buf
|
struct header_buf
|
||||||
{
|
{
|
||||||
unsigned off;
|
unsigned off;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue