From a37117a98b22e1fc91b019d973d7c02b5b72f981 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 18 Dec 2019 14:23:05 +0900 Subject: [PATCH] QUIC UDP GSO --- src/h2load.cc | 39 +++++++++++++++++++++++++++++--- src/h2load.h | 4 +++- src/h2load_http3_session.cc | 9 ++++++++ src/h2load_http3_session.h | 1 + src/h2load_quic.cc | 45 ++++++++++++++++++++++++++++++------- 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/h2load.cc b/src/h2load.cc index abe9332b..d8c2e5f6 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -35,6 +35,7 @@ # include #endif // HAVE_FCNTL_H #include +#include #include #include @@ -117,7 +118,8 @@ Config::Config() timing_script(false), base_uri_unix(false), unix_addr{}, - rps(0.) {} + rps(0.), + no_udp_gso(false) {} Config::~Config() { if (addrs) { @@ -1375,8 +1377,32 @@ int Client::write_tls() { } int Client::write_udp(const sockaddr *addr, socklen_t addrlen, - const uint8_t *data, size_t datalen) { - auto nwrite = sendto(fd, data, datalen, MSG_DONTWAIT, addr, addrlen); + const uint8_t *data, size_t datalen, size_t gso_size) { + iovec msg_iov; + msg_iov.iov_base = const_cast(data); + msg_iov.iov_len = datalen; + + msghdr msg{}; + msg.msg_name = const_cast(addr); + msg.msg_namelen = addrlen; + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + +#ifdef UDP_SEGMENT + std::array 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(CMSG_DATA(cm))) = gso_size; + } +#endif // UDP_SEGMENT + + auto nwrite = sendmsg(fd, &msg, 0); if (nwrite < 0) { std::cerr << "sendto: errno=" << errno << std::endl; } else { @@ -2183,6 +2209,8 @@ Options: Specify the supported groups. Default: )" << config.groups << R"( + --no-udp-gso + Disable UDP GSO. -v, --verbose Output debug information. --version Display version information and exit. @@ -2245,6 +2273,7 @@ int main(int argc, char **argv) { {"rps", required_argument, &flag, 12}, {"groups", required_argument, &flag, 13}, {"tls13-ciphers", required_argument, &flag, 14}, + {"no-udp-gso", no_argument, &flag, 15}, {nullptr, 0, nullptr, 0}}; int option_index = 0; auto c = getopt_long(argc, argv, @@ -2503,6 +2532,10 @@ int main(int argc, char **argv) { // --tls13-ciphers config.tls13_ciphers = optarg; break; + case 15: + // --no-udp-gso + config.no_udp_gso = true; + break; } break; default: diff --git a/src/h2load.h b/src/h2load.h index 348fcdc9..afb369a6 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -125,6 +125,8 @@ struct Config { std::vector npn_list; // The number of request per second for each client. double rps; + // Disables GSO for UDP connections. + bool no_udp_gso; Config(); ~Config(); @@ -458,7 +460,7 @@ struct Client { int read_quic(); int write_quic(); 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(); int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level, diff --git a/src/h2load_http3_session.cc b/src/h2load_http3_session.cc index ca36a3a8..881cbe66 100644 --- a/src/h2load_http3_session.cc +++ b/src/h2load_http3_session.cc @@ -402,6 +402,15 @@ int Http3Session::block_stream(int64_t stream_id) { 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) { auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen); if (rv != 0) { diff --git a/src/h2load_http3_session.h b/src/h2load_http3_session.h index aaca13dc..db6c4244 100644 --- a/src/h2load_http3_session.h +++ b/src/h2load_http3_session.h @@ -63,6 +63,7 @@ public: ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec, size_t veccnt); 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_ack_offset(int64_t stream_id, size_t datalen); diff --git a/src/h2load_quic.cc b/src/h2load_quic.cc index 7a8c262d..9d8693f0 100644 --- a/src/h2load_quic.cc +++ b/src/h2load_quic.cc @@ -24,6 +24,8 @@ */ #include "h2load_quic.h" +#include + #include #include @@ -425,7 +427,7 @@ void Client::quic_close_connection() { } write_udp(reinterpret_cast(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, @@ -554,7 +556,18 @@ int Client::write_quic() { } std::array vec; - std::array buf; + size_t pktcnt = 0; + size_t max_pktcnt = +#ifdef UDP_SEGMENT + worker->config->no_udp_gso + ? 1 + : std::min(static_cast(10), + static_cast(64_k / quic.max_pktlen)); +#else // !UDP_SEGMENT + 1; +#endif // !UDP_SEGMENT + std::array buf; + uint8_t *bufpos = buf.data(); ngtcp2_path_storage ps; ngtcp2_path_storage_zero(&ps); @@ -583,16 +596,20 @@ int Client::write_quic() { } auto nwrite = ngtcp2_conn_writev_stream( - quic.conn, &ps.path, nullptr, buf.data(), quic.max_pktlen, &ndatalen, - flags, stream_id, reinterpret_cast(v), vcnt, + quic.conn, &ps.path, nullptr, bufpos, quic.max_pktlen, &ndatalen, flags, + stream_id, reinterpret_cast(v), vcnt, timestamp(worker->loop)); if (nwrite < 0) { switch (nwrite) { 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: assert(ndatalen == -1); - - if (s->block_stream(stream_id) != 0) { + if (s->shutdown_stream_write(stream_id) != 0) { return -1; } continue; @@ -613,11 +630,23 @@ int Client::write_quic() { quic_restart_pkt_timer(); 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; } - write_udp(reinterpret_cast(ps.path.remote.addr), - ps.path.remote.addrlen, buf.data(), nwrite); + bufpos += nwrite; + + // Assume that the path does not change. + if (++pktcnt == max_pktcnt || + static_cast(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; + } } }