QUIC UDP GSO

This commit is contained in:
Tatsuhiro Tsujikawa 2019-12-18 14:23:05 +09:00
parent eb1607890a
commit a37117a98b
5 changed files with 86 additions and 12 deletions

View File

@ -35,6 +35,7 @@
# include <fcntl.h> # include <fcntl.h>
#endif // HAVE_FCNTL_H #endif // HAVE_FCNTL_H
#include <sys/mman.h> #include <sys/mman.h>
#include <netinet/udp.h>
#include <cstdio> #include <cstdio>
#include <cassert> #include <cassert>
@ -117,7 +118,8 @@ Config::Config()
timing_script(false), timing_script(false),
base_uri_unix(false), base_uri_unix(false),
unix_addr{}, unix_addr{},
rps(0.) {} rps(0.),
no_udp_gso(false) {}
Config::~Config() { Config::~Config() {
if (addrs) { if (addrs) {
@ -1375,8 +1377,32 @@ int Client::write_tls() {
} }
int Client::write_udp(const sockaddr *addr, socklen_t addrlen, int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
const uint8_t *data, size_t datalen) { const uint8_t *data, size_t datalen, size_t gso_size) {
auto nwrite = sendto(fd, data, datalen, MSG_DONTWAIT, addr, addrlen); iovec msg_iov;
msg_iov.iov_base = const_cast<uint8_t *>(data);
msg_iov.iov_len = datalen;
msghdr msg{};
msg.msg_name = const_cast<sockaddr *>(addr);
msg.msg_namelen = addrlen;
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
#ifdef UDP_SEGMENT
std::array<uint8_t, CMSG_SPACE(sizeof(uint16_t))> msg_ctrl{};
if (gso_size && datalen > gso_size) {
msg.msg_control = msg_ctrl.data();
msg.msg_controllen = msg_ctrl.size();
auto cm = CMSG_FIRSTHDR(&msg);
cm->cmsg_level = SOL_UDP;
cm->cmsg_type = UDP_SEGMENT;
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
*(reinterpret_cast<uint16_t *>(CMSG_DATA(cm))) = gso_size;
}
#endif // UDP_SEGMENT
auto nwrite = sendmsg(fd, &msg, 0);
if (nwrite < 0) { if (nwrite < 0) {
std::cerr << "sendto: errno=" << errno << std::endl; std::cerr << "sendto: errno=" << errno << std::endl;
} else { } else {
@ -2183,6 +2209,8 @@ Options:
Specify the supported groups. Specify the supported groups.
Default: )" Default: )"
<< config.groups << R"( << config.groups << R"(
--no-udp-gso
Disable UDP GSO.
-v, --verbose -v, --verbose
Output debug information. Output debug information.
--version Display version information and exit. --version Display version information and exit.
@ -2245,6 +2273,7 @@ int main(int argc, char **argv) {
{"rps", required_argument, &flag, 12}, {"rps", required_argument, &flag, 12},
{"groups", required_argument, &flag, 13}, {"groups", required_argument, &flag, 13},
{"tls13-ciphers", required_argument, &flag, 14}, {"tls13-ciphers", required_argument, &flag, 14},
{"no-udp-gso", no_argument, &flag, 15},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
auto c = getopt_long(argc, argv, auto c = getopt_long(argc, argv,
@ -2503,6 +2532,10 @@ int main(int argc, char **argv) {
// --tls13-ciphers // --tls13-ciphers
config.tls13_ciphers = optarg; config.tls13_ciphers = optarg;
break; break;
case 15:
// --no-udp-gso
config.no_udp_gso = true;
break;
} }
break; break;
default: default:

View File

@ -125,6 +125,8 @@ struct Config {
std::vector<std::string> npn_list; std::vector<std::string> npn_list;
// The number of request per second for each client. // The number of request per second for each client.
double rps; double rps;
// Disables GSO for UDP connections.
bool no_udp_gso;
Config(); Config();
~Config(); ~Config();
@ -458,7 +460,7 @@ struct Client {
int read_quic(); int read_quic();
int write_quic(); int write_quic();
int write_udp(const sockaddr *addr, socklen_t addrlen, const uint8_t *data, int write_udp(const sockaddr *addr, socklen_t addrlen, const uint8_t *data,
size_t datalen); size_t datalen, size_t gso_size);
void quic_close_connection(); void quic_close_connection();
int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level, int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level,

View File

@ -402,6 +402,15 @@ int Http3Session::block_stream(int64_t stream_id) {
return 0; return 0;
} }
int Http3Session::shutdown_stream_write(int64_t stream_id) {
auto rv = nghttp3_conn_shutdown_stream_write(conn_, stream_id);
if (rv != 0) {
client_->quic.last_error = quic::err_application(rv);
return -1;
}
return 0;
}
int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) { int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen); auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
if (rv != 0) { if (rv != 0) {

View File

@ -63,6 +63,7 @@ public:
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec, ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
size_t veccnt); size_t veccnt);
int block_stream(int64_t stream_id); int block_stream(int64_t stream_id);
int shutdown_stream_write(int64_t stream_id);
int add_write_offset(int64_t stream_id, size_t ndatalen); int add_write_offset(int64_t stream_id, size_t ndatalen);
int add_ack_offset(int64_t stream_id, size_t datalen); int add_ack_offset(int64_t stream_id, size_t datalen);

View File

@ -24,6 +24,8 @@
*/ */
#include "h2load_quic.h" #include "h2load_quic.h"
#include <netinet/udp.h>
#include <iostream> #include <iostream>
#include <ngtcp2/ngtcp2_crypto_openssl.h> #include <ngtcp2/ngtcp2_crypto_openssl.h>
@ -425,7 +427,7 @@ void Client::quic_close_connection() {
} }
write_udp(reinterpret_cast<sockaddr *>(ps.path.remote.addr), write_udp(reinterpret_cast<sockaddr *>(ps.path.remote.addr),
ps.path.remote.addrlen, buf.data(), nwrite); ps.path.remote.addrlen, buf.data(), nwrite, 0);
} }
int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret, int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
@ -554,7 +556,18 @@ int Client::write_quic() {
} }
std::array<nghttp3_vec, 16> vec; std::array<nghttp3_vec, 16> vec;
std::array<uint8_t, 1500> buf; size_t pktcnt = 0;
size_t max_pktcnt =
#ifdef UDP_SEGMENT
worker->config->no_udp_gso
? 1
: std::min(static_cast<size_t>(10),
static_cast<size_t>(64_k / quic.max_pktlen));
#else // !UDP_SEGMENT
1;
#endif // !UDP_SEGMENT
std::array<uint8_t, 64_k> buf;
uint8_t *bufpos = buf.data();
ngtcp2_path_storage ps; ngtcp2_path_storage ps;
ngtcp2_path_storage_zero(&ps); ngtcp2_path_storage_zero(&ps);
@ -583,16 +596,20 @@ int Client::write_quic() {
} }
auto nwrite = ngtcp2_conn_writev_stream( auto nwrite = ngtcp2_conn_writev_stream(
quic.conn, &ps.path, nullptr, buf.data(), quic.max_pktlen, &ndatalen, quic.conn, &ps.path, nullptr, bufpos, quic.max_pktlen, &ndatalen, flags,
flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
timestamp(worker->loop)); timestamp(worker->loop));
if (nwrite < 0) { if (nwrite < 0) {
switch (nwrite) { switch (nwrite) {
case NGTCP2_ERR_STREAM_DATA_BLOCKED: case NGTCP2_ERR_STREAM_DATA_BLOCKED:
assert(ndatalen == -1);
if (s->block_stream(stream_id) != 0) {
return -1;
}
continue;
case NGTCP2_ERR_STREAM_SHUT_WR: case NGTCP2_ERR_STREAM_SHUT_WR:
assert(ndatalen == -1); assert(ndatalen == -1);
if (s->shutdown_stream_write(stream_id) != 0) {
if (s->block_stream(stream_id) != 0) {
return -1; return -1;
} }
continue; continue;
@ -613,11 +630,23 @@ int Client::write_quic() {
quic_restart_pkt_timer(); quic_restart_pkt_timer();
if (nwrite == 0) { if (nwrite == 0) {
if (bufpos - buf.data()) {
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
bufpos - buf.data(), quic.max_pktlen);
}
return 0; return 0;
} }
write_udp(reinterpret_cast<sockaddr *>(ps.path.remote.addr), bufpos += nwrite;
ps.path.remote.addrlen, buf.data(), nwrite);
// Assume that the path does not change.
if (++pktcnt == max_pktcnt ||
static_cast<size_t>(nwrite) < quic.max_pktlen) {
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
bufpos - buf.data(), quic.max_pktlen);
signal_write();
return 0;
}
} }
} }