QUIC sockets return WRITE_STATUS_BLOCKED on ENOBUFS. Protected by FLAGS_quic_reloadable_flag_quic_enobufs_blocekd. PiperOrigin-RevId: 765182479
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h index 09e6b2a..de37bdf 100755 --- a/quiche/common/quiche_feature_flags_list.h +++ b/quiche/common/quiche_feature_flags_list.h
@@ -34,6 +34,7 @@ QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_mtu_discovery_at_server, false, false, "If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes.") QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_server_on_wire_ping, true, true, "If true, enable server retransmittable on wire PING.") QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_version_rfcv2, false, false, "When true, support RFC9369.") +QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enobufs_blocked, false, false, "If true, ENOBUFS socket errors are reported as socket blocked instead of socket failure.") QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fin_before_completed_http_headers, false, true, "If true, close the connection with error if FIN is received before finish receiving the whole HTTP headers.") QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fix_timeouts, true, true, "If true, postpone setting handshake timeout to infinite to handshake complete.") QUICHE_FLAG(bool, quiche_reloadable_flag_quic_heapless_key_derivation, false, false, "If true, QUIC key derivation uses heapless crypto utils.")
diff --git a/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc b/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc index 1d5a80f..6e0574d 100644 --- a/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc +++ b/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc
@@ -12,7 +12,7 @@ #include "quiche/quic/core/flow_label.h" #include "quiche/quic/core/quic_linux_socket_utils.h" -#include "quiche/quic/platform/api/quic_server_stats.h" +#include "quiche/quic/platform/api/quic_flags.h" namespace quic { @@ -32,7 +32,8 @@ supports_release_time_( GetQuicRestartFlag(quic_support_release_time_for_gso) && QuicLinuxSocketUtils::EnableReleaseTime(fd, - clockid_for_release_time)) { + clockid_for_release_time)), + enobufs_blocked_(GetQuicReloadableFlag(quic_enobufs_blocked)) { if (supports_release_time_) { QUIC_RESTART_FLAG_COUNT(quic_support_release_time_for_gso); }
diff --git a/quiche/quic/core/batch_writer/quic_gso_batch_writer.h b/quiche/quic/core/batch_writer/quic_gso_batch_writer.h index 5e82952..b2d560f 100644 --- a/quiche/quic/core/batch_writer/quic_gso_batch_writer.h +++ b/quiche/quic/core/batch_writer/quic_gso_batch_writer.h
@@ -85,7 +85,8 @@ cmsg_builder(&hdr, first.self_address, gso_size, first.release_time, first.params.ecn_codepoint, first.params.flow_label); - write_result = QuicLinuxSocketUtils::WritePacket(fd(), hdr); + write_result = + QuicLinuxSocketUtils::WritePacket(fd(), hdr, enobufs_blocked_); QUIC_DVLOG(1) << "Write GSO packet result: " << write_result << ", fd: " << fd() << ", self_address: " << first.self_address.ToString() @@ -118,6 +119,8 @@ const clockid_t clockid_for_release_time_; const bool supports_release_time_; + + const bool enobufs_blocked_ = false; }; } // namespace quic
diff --git a/quiche/quic/core/quic_linux_socket_utils.cc b/quiche/quic/core/quic_linux_socket_utils.cc index 8134eea..c56d939 100644 --- a/quiche/quic/core/quic_linux_socket_utils.cc +++ b/quiche/quic/core/quic_linux_socket_utils.cc
@@ -7,11 +7,13 @@ #include <linux/net_tstamp.h> #include <netinet/in.h> +#include <cerrno> #include <cstddef> #include <cstdint> #include <string> #include "quiche/quic/core/quic_syscall_wrapper.h" +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_flag_utils.h" #include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_logging.h" @@ -263,7 +265,8 @@ } // static -WriteResult QuicLinuxSocketUtils::WritePacket(int fd, const QuicMsgHdr& hdr) { +WriteResult QuicLinuxSocketUtils::WritePacket(int fd, const QuicMsgHdr& hdr, + bool enobufs_blocked) { int rc; do { rc = GetGlobalSyscallWrapper()->Sendmsg(fd, hdr.hdr(), 0); @@ -273,7 +276,11 @@ } if (errno == ENOBUFS) { QUIC_CODE_COUNT(quic_sendmsg_enobufs); - errno = ENOBUFS; + if (enobufs_blocked) { + return WriteResult(WRITE_STATUS_BLOCKED, errno); + } else { + errno = ENOBUFS; + } } return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) ? WRITE_STATUS_BLOCKED
diff --git a/quiche/quic/core/quic_linux_socket_utils.h b/quiche/quic/core/quic_linux_socket_utils.h index f125cba..1c0b86d 100644 --- a/quiche/quic/core/quic_linux_socket_utils.h +++ b/quiche/quic/core/quic_linux_socket_utils.h
@@ -281,7 +281,8 @@ cmsghdr* cmsg); // Writes the packet in |hdr| to the socket, using ::sendmsg. - static WriteResult WritePacket(int fd, const QuicMsgHdr& hdr); + static WriteResult WritePacket(int fd, const QuicMsgHdr& hdr, + bool enobufs_blocked); // Writes the packets in |mhdr| to the socket, using ::sendmmsg if available. static WriteResult WriteMultiplePackets(int fd, QuicMMsgHdr* mhdr,
diff --git a/quiche/quic/core/quic_linux_socket_utils_test.cc b/quiche/quic/core/quic_linux_socket_utils_test.cc index 93ebf01..1a910fe 100644 --- a/quiche/quic/core/quic_linux_socket_utils_test.cc +++ b/quiche/quic/core/quic_linux_socket_utils_test.cc
@@ -7,11 +7,13 @@ #include <netinet/in.h> #include <stdint.h> +#include <cerrno> #include <cstddef> #include <sstream> #include <string> #include <vector> +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/test_tools/quic_mock_syscall_wrapper.h" #include "quiche/common/quiche_circular_deque.h" @@ -19,6 +21,7 @@ using testing::_; using testing::InSequence; using testing::Invoke; +using testing::SetErrnoAndReturn; namespace quic { namespace test { @@ -323,6 +326,16 @@ } } +TEST_F(QuicLinuxSocketUtilsTest, WriteReturnsEnobufs) { + QuicMsgHdr hdr(nullptr, 0, nullptr, 0); + EXPECT_CALL(mock_syscalls_, Sendmsg) + .WillRepeatedly(SetErrnoAndReturn(ENOBUFS, -1)); + EXPECT_EQ(WriteResult(WRITE_STATUS_BLOCKED, ENOBUFS), + QuicLinuxSocketUtils::WritePacket(/*fd=*/1, hdr, true)); + EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, ENOBUFS), + QuicLinuxSocketUtils::WritePacket(/*fd=*/1, hdr, false)); +} + } // namespace } // namespace test } // namespace quic