1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/batch_writer/quic_gso_batch_writer.h"
6
7 #include <cstdint>
8 #include <limits>
9 #include <memory>
10 #include <utility>
11
12 #include "quiche/quic/platform/api/quic_ip_address.h"
13 #include "quiche/quic/platform/api/quic_test.h"
14 #include "quiche/quic/test_tools/quic_mock_syscall_wrapper.h"
15
16 using testing::_;
17 using testing::Invoke;
18 using testing::StrictMock;
19
20 namespace quic {
21 namespace test {
22 namespace {
23
PacketLength(const msghdr * msg)24 size_t PacketLength(const msghdr* msg) {
25 size_t length = 0;
26 for (size_t i = 0; i < msg->msg_iovlen; ++i) {
27 length += msg->msg_iov[i].iov_len;
28 }
29 return length;
30 }
31
MillisToNanos(uint64_t milliseconds)32 uint64_t MillisToNanos(uint64_t milliseconds) { return milliseconds * 1000000; }
33
34 class QUIC_EXPORT_PRIVATE TestQuicGsoBatchWriter : public QuicGsoBatchWriter {
35 public:
36 using QuicGsoBatchWriter::batch_buffer;
37 using QuicGsoBatchWriter::buffered_writes;
38 using QuicGsoBatchWriter::CanBatch;
39 using QuicGsoBatchWriter::CanBatchResult;
40 using QuicGsoBatchWriter::GetReleaseTime;
41 using QuicGsoBatchWriter::MaxSegments;
42 using QuicGsoBatchWriter::QuicGsoBatchWriter;
43 using QuicGsoBatchWriter::ReleaseTime;
44
45 static std::unique_ptr<TestQuicGsoBatchWriter>
NewInstanceWithReleaseTimeSupport()46 NewInstanceWithReleaseTimeSupport() {
47 return std::unique_ptr<TestQuicGsoBatchWriter>(new TestQuicGsoBatchWriter(
48 std::make_unique<QuicBatchWriterBuffer>(),
49 /*fd=*/-1, CLOCK_MONOTONIC, ReleaseTimeForceEnabler()));
50 }
51
NowInNanosForReleaseTime() const52 uint64_t NowInNanosForReleaseTime() const override {
53 return MillisToNanos(forced_release_time_ms_);
54 }
55
ForceReleaseTimeMs(uint64_t forced_release_time_ms)56 void ForceReleaseTimeMs(uint64_t forced_release_time_ms) {
57 forced_release_time_ms_ = forced_release_time_ms;
58 }
59
60 private:
61 uint64_t forced_release_time_ms_ = 1;
62 };
63
64 struct QUIC_EXPORT_PRIVATE TestPerPacketOptions : public PerPacketOptions {
Clonequic::test::__anon1373e6820111::TestPerPacketOptions65 std::unique_ptr<quic::PerPacketOptions> Clone() const override {
66 return std::make_unique<TestPerPacketOptions>(*this);
67 }
68 };
69
70 // TestBufferedWrite is a copy-constructible BufferedWrite.
71 struct QUIC_EXPORT_PRIVATE TestBufferedWrite : public BufferedWrite {
72 using BufferedWrite::BufferedWrite;
TestBufferedWritequic::test::__anon1373e6820111::TestBufferedWrite73 TestBufferedWrite(const TestBufferedWrite& other)
74 : BufferedWrite(other.buffer, other.buf_len, other.self_address,
75 other.peer_address,
76 other.options ? other.options->Clone()
77 : std::unique_ptr<PerPacketOptions>(),
78 other.release_time) {}
79 };
80
81 // Pointed to by all instances of |BatchCriteriaTestData|. Content not used.
82 static char unused_packet_buffer[kMaxOutgoingPacketSize];
83
84 struct QUIC_EXPORT_PRIVATE BatchCriteriaTestData {
BatchCriteriaTestDataquic::test::__anon1373e6820111::BatchCriteriaTestData85 BatchCriteriaTestData(size_t buf_len, const QuicIpAddress& self_address,
86 const QuicSocketAddress& peer_address,
87 uint64_t release_time, bool can_batch, bool must_flush)
88 : buffered_write(unused_packet_buffer, buf_len, self_address,
89 peer_address, std::unique_ptr<PerPacketOptions>(),
90 release_time),
91 can_batch(can_batch),
92 must_flush(must_flush) {}
93
94 TestBufferedWrite buffered_write;
95 // Expected value of CanBatchResult.can_batch when batching |buffered_write|.
96 bool can_batch;
97 // Expected value of CanBatchResult.must_flush when batching |buffered_write|.
98 bool must_flush;
99 };
100
BatchCriteriaTestData_SizeDecrease()101 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_SizeDecrease() {
102 const QuicIpAddress self_addr;
103 const QuicSocketAddress peer_addr;
104 std::vector<BatchCriteriaTestData> test_data_table = {
105 // clang-format off
106 // buf_len self_addr peer_addr t_rel can_batch must_flush
107 {1350, self_addr, peer_addr, 0, true, false},
108 {1350, self_addr, peer_addr, 0, true, false},
109 {1350, self_addr, peer_addr, 0, true, false},
110 {39, self_addr, peer_addr, 0, true, true},
111 {39, self_addr, peer_addr, 0, false, true},
112 {1350, self_addr, peer_addr, 0, false, true},
113 // clang-format on
114 };
115 return test_data_table;
116 }
117
BatchCriteriaTestData_SizeIncrease()118 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_SizeIncrease() {
119 const QuicIpAddress self_addr;
120 const QuicSocketAddress peer_addr;
121 std::vector<BatchCriteriaTestData> test_data_table = {
122 // clang-format off
123 // buf_len self_addr peer_addr t_rel can_batch must_flush
124 {1350, self_addr, peer_addr, 0, true, false},
125 {1350, self_addr, peer_addr, 0, true, false},
126 {1350, self_addr, peer_addr, 0, true, false},
127 {1351, self_addr, peer_addr, 0, false, true},
128 // clang-format on
129 };
130 return test_data_table;
131 }
132
BatchCriteriaTestData_AddressChange()133 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_AddressChange() {
134 const QuicIpAddress self_addr1 = QuicIpAddress::Loopback4();
135 const QuicIpAddress self_addr2 = QuicIpAddress::Loopback6();
136 const QuicSocketAddress peer_addr1(self_addr1, 666);
137 const QuicSocketAddress peer_addr2(self_addr1, 777);
138 const QuicSocketAddress peer_addr3(self_addr2, 666);
139 const QuicSocketAddress peer_addr4(self_addr2, 777);
140 std::vector<BatchCriteriaTestData> test_data_table = {
141 // clang-format off
142 // buf_len self_addr peer_addr t_rel can_batch must_flush
143 {1350, self_addr1, peer_addr1, 0, true, false},
144 {1350, self_addr1, peer_addr1, 0, true, false},
145 {1350, self_addr1, peer_addr1, 0, true, false},
146 {1350, self_addr2, peer_addr1, 0, false, true},
147 {1350, self_addr1, peer_addr2, 0, false, true},
148 {1350, self_addr1, peer_addr3, 0, false, true},
149 {1350, self_addr1, peer_addr4, 0, false, true},
150 {1350, self_addr1, peer_addr4, 0, false, true},
151 // clang-format on
152 };
153 return test_data_table;
154 }
155
BatchCriteriaTestData_ReleaseTime1()156 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_ReleaseTime1() {
157 const QuicIpAddress self_addr;
158 const QuicSocketAddress peer_addr;
159 std::vector<BatchCriteriaTestData> test_data_table = {
160 // clang-format off
161 // buf_len self_addr peer_addr t_rel can_batch must_flush
162 {1350, self_addr, peer_addr, 5, true, false},
163 {1350, self_addr, peer_addr, 5, true, false},
164 {1350, self_addr, peer_addr, 5, true, false},
165 {1350, self_addr, peer_addr, 9, false, true},
166 // clang-format on
167 };
168 return test_data_table;
169 }
170
BatchCriteriaTestData_ReleaseTime2()171 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_ReleaseTime2() {
172 const QuicIpAddress self_addr;
173 const QuicSocketAddress peer_addr;
174 std::vector<BatchCriteriaTestData> test_data_table = {
175 // clang-format off
176 // buf_len self_addr peer_addr t_rel can_batch must_flush
177 {1350, self_addr, peer_addr, 0, true, false},
178 {1350, self_addr, peer_addr, 0, true, false},
179 {1350, self_addr, peer_addr, 0, true, false},
180 {1350, self_addr, peer_addr, 9, false, true},
181 // clang-format on
182 };
183 return test_data_table;
184 }
185
BatchCriteriaTestData_MaxSegments(size_t gso_size)186 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_MaxSegments(
187 size_t gso_size) {
188 const QuicIpAddress self_addr;
189 const QuicSocketAddress peer_addr;
190 std::vector<BatchCriteriaTestData> test_data_table;
191 size_t max_segments = TestQuicGsoBatchWriter::MaxSegments(gso_size);
192 for (size_t i = 0; i < max_segments; ++i) {
193 bool is_last_in_batch = (i + 1 == max_segments);
194 test_data_table.push_back({gso_size, self_addr, peer_addr,
195 /*release_time=*/0, true, is_last_in_batch});
196 }
197 test_data_table.push_back(
198 {gso_size, self_addr, peer_addr, /*release_time=*/0, false, true});
199 return test_data_table;
200 }
201
202 class QuicGsoBatchWriterTest : public QuicTest {
203 protected:
WritePacket(QuicGsoBatchWriter * writer,size_t packet_size)204 WriteResult WritePacket(QuicGsoBatchWriter* writer, size_t packet_size) {
205 return writer->WritePacket(&packet_buffer_[0], packet_size, self_address_,
206 peer_address_, nullptr);
207 }
208
WritePacketWithOptions(QuicGsoBatchWriter * writer,PerPacketOptions * options)209 WriteResult WritePacketWithOptions(QuicGsoBatchWriter* writer,
210 PerPacketOptions* options) {
211 return writer->WritePacket(&packet_buffer_[0], 1350, self_address_,
212 peer_address_, options);
213 }
214
215 QuicIpAddress self_address_ = QuicIpAddress::Any4();
216 QuicSocketAddress peer_address_{QuicIpAddress::Any4(), 443};
217 char packet_buffer_[1500];
218 StrictMock<MockQuicSyscallWrapper> mock_syscalls_;
219 ScopedGlobalSyscallWrapperOverride syscall_override_{&mock_syscalls_};
220 };
221
TEST_F(QuicGsoBatchWriterTest,BatchCriteria)222 TEST_F(QuicGsoBatchWriterTest, BatchCriteria) {
223 std::unique_ptr<TestQuicGsoBatchWriter> writer;
224
225 std::vector<std::vector<BatchCriteriaTestData>> test_data_tables;
226 test_data_tables.emplace_back(BatchCriteriaTestData_SizeDecrease());
227 test_data_tables.emplace_back(BatchCriteriaTestData_SizeIncrease());
228 test_data_tables.emplace_back(BatchCriteriaTestData_AddressChange());
229 test_data_tables.emplace_back(BatchCriteriaTestData_ReleaseTime1());
230 test_data_tables.emplace_back(BatchCriteriaTestData_ReleaseTime2());
231 test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(1));
232 test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(2));
233 test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(1350));
234
235 for (size_t i = 0; i < test_data_tables.size(); ++i) {
236 writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
237
238 const auto& test_data_table = test_data_tables[i];
239 for (size_t j = 0; j < test_data_table.size(); ++j) {
240 const BatchCriteriaTestData& test_data = test_data_table[j];
241 SCOPED_TRACE(testing::Message() << "i=" << i << ", j=" << j);
242 TestPerPacketOptions options;
243 options.release_time_delay = QuicTime::Delta::FromMicroseconds(
244 test_data.buffered_write.release_time);
245 TestQuicGsoBatchWriter::CanBatchResult result = writer->CanBatch(
246 test_data.buffered_write.buffer, test_data.buffered_write.buf_len,
247 test_data.buffered_write.self_address,
248 test_data.buffered_write.peer_address, &options,
249 test_data.buffered_write.release_time);
250
251 ASSERT_EQ(test_data.can_batch, result.can_batch);
252 ASSERT_EQ(test_data.must_flush, result.must_flush);
253
254 if (result.can_batch) {
255 ASSERT_TRUE(writer->batch_buffer()
256 .PushBufferedWrite(
257 test_data.buffered_write.buffer,
258 test_data.buffered_write.buf_len,
259 test_data.buffered_write.self_address,
260 test_data.buffered_write.peer_address, &options,
261 test_data.buffered_write.release_time)
262 .succeeded);
263 }
264 }
265 }
266 }
267
TEST_F(QuicGsoBatchWriterTest,WriteSuccess)268 TEST_F(QuicGsoBatchWriterTest, WriteSuccess) {
269 TestQuicGsoBatchWriter writer(/*fd=*/-1);
270
271 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 1000));
272
273 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
274 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
275 EXPECT_EQ(1100u, PacketLength(msg));
276 return 1100;
277 }));
278 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 1100), WritePacket(&writer, 100));
279 ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
280 ASSERT_EQ(0u, writer.buffered_writes().size());
281 }
282
TEST_F(QuicGsoBatchWriterTest,WriteBlockDataNotBuffered)283 TEST_F(QuicGsoBatchWriterTest, WriteBlockDataNotBuffered) {
284 TestQuicGsoBatchWriter writer(/*fd=*/-1);
285
286 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
287 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
288
289 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
290 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
291 EXPECT_EQ(200u, PacketLength(msg));
292 errno = EWOULDBLOCK;
293 return -1;
294 }));
295 ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK),
296 WritePacket(&writer, 150));
297 ASSERT_EQ(200u, writer.batch_buffer().SizeInUse());
298 ASSERT_EQ(2u, writer.buffered_writes().size());
299 }
300
TEST_F(QuicGsoBatchWriterTest,WriteBlockDataBuffered)301 TEST_F(QuicGsoBatchWriterTest, WriteBlockDataBuffered) {
302 TestQuicGsoBatchWriter writer(/*fd=*/-1);
303
304 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
305 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
306
307 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
308 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
309 EXPECT_EQ(250u, PacketLength(msg));
310 errno = EWOULDBLOCK;
311 return -1;
312 }));
313 ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED_DATA_BUFFERED, EWOULDBLOCK),
314 WritePacket(&writer, 50));
315
316 EXPECT_TRUE(writer.IsWriteBlocked());
317
318 ASSERT_EQ(250u, writer.batch_buffer().SizeInUse());
319 ASSERT_EQ(3u, writer.buffered_writes().size());
320 }
321
TEST_F(QuicGsoBatchWriterTest,WriteErrorWithoutDataBuffered)322 TEST_F(QuicGsoBatchWriterTest, WriteErrorWithoutDataBuffered) {
323 TestQuicGsoBatchWriter writer(/*fd=*/-1);
324
325 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
326 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
327
328 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
329 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
330 EXPECT_EQ(200u, PacketLength(msg));
331 errno = EPERM;
332 return -1;
333 }));
334 WriteResult error_result = WritePacket(&writer, 150);
335 ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), error_result);
336
337 ASSERT_EQ(3u, error_result.dropped_packets);
338 ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
339 ASSERT_EQ(0u, writer.buffered_writes().size());
340 }
341
TEST_F(QuicGsoBatchWriterTest,WriteErrorAfterDataBuffered)342 TEST_F(QuicGsoBatchWriterTest, WriteErrorAfterDataBuffered) {
343 TestQuicGsoBatchWriter writer(/*fd=*/-1);
344
345 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
346 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
347
348 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
349 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
350 EXPECT_EQ(250u, PacketLength(msg));
351 errno = EPERM;
352 return -1;
353 }));
354 WriteResult error_result = WritePacket(&writer, 50);
355 ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), error_result);
356
357 ASSERT_EQ(3u, error_result.dropped_packets);
358 ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
359 ASSERT_EQ(0u, writer.buffered_writes().size());
360 }
361
TEST_F(QuicGsoBatchWriterTest,FlushError)362 TEST_F(QuicGsoBatchWriterTest, FlushError) {
363 TestQuicGsoBatchWriter writer(/*fd=*/-1);
364
365 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
366 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
367
368 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
369 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
370 EXPECT_EQ(200u, PacketLength(msg));
371 errno = EINVAL;
372 return -1;
373 }));
374 WriteResult error_result = writer.Flush();
375 ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EINVAL), error_result);
376
377 ASSERT_EQ(2u, error_result.dropped_packets);
378 ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
379 ASSERT_EQ(0u, writer.buffered_writes().size());
380 }
381
TEST_F(QuicGsoBatchWriterTest,ReleaseTimeNullOptions)382 TEST_F(QuicGsoBatchWriterTest, ReleaseTimeNullOptions) {
383 auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
384 EXPECT_EQ(0u, writer->GetReleaseTime(nullptr).actual_release_time);
385 }
386
TEST_F(QuicGsoBatchWriterTest,ReleaseTime)387 TEST_F(QuicGsoBatchWriterTest, ReleaseTime) {
388 const WriteResult write_buffered(WRITE_STATUS_OK, 0);
389
390 auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
391
392 TestPerPacketOptions options;
393 EXPECT_TRUE(options.release_time_delay.IsZero());
394 EXPECT_FALSE(options.allow_burst);
395 EXPECT_EQ(MillisToNanos(1),
396 writer->GetReleaseTime(&options).actual_release_time);
397
398 // The 1st packet has no delay.
399 WriteResult result = WritePacketWithOptions(writer.get(), &options);
400 ASSERT_EQ(write_buffered, result);
401 EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time);
402 EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
403
404 // The 2nd packet has some delay, but allows burst.
405 options.release_time_delay = QuicTime::Delta::FromMilliseconds(3);
406 options.allow_burst = true;
407 result = WritePacketWithOptions(writer.get(), &options);
408 ASSERT_EQ(write_buffered, result);
409 EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time);
410 EXPECT_EQ(result.send_time_offset, QuicTime::Delta::FromMilliseconds(-3));
411
412 // The 3rd packet has more delay and does not allow burst.
413 // The first 2 packets are flushed due to different release time.
414 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
415 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
416 EXPECT_EQ(2700u, PacketLength(msg));
417 errno = 0;
418 return 0;
419 }));
420 options.release_time_delay = QuicTime::Delta::FromMilliseconds(5);
421 options.allow_burst = false;
422 result = WritePacketWithOptions(writer.get(), &options);
423 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 2700), result);
424 EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time);
425 EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
426
427 // The 4th packet has same delay, but allows burst.
428 options.allow_burst = true;
429 result = WritePacketWithOptions(writer.get(), &options);
430 ASSERT_EQ(write_buffered, result);
431 EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time);
432 EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
433
434 // The 5th packet has same delay, allows burst, but is shorter.
435 // Packets 3,4 and 5 are flushed.
436 EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
437 .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
438 EXPECT_EQ(3000u, PacketLength(msg));
439 errno = 0;
440 return 0;
441 }));
442 options.allow_burst = true;
443 EXPECT_EQ(MillisToNanos(6),
444 writer->GetReleaseTime(&options).actual_release_time);
445 ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 3000),
446 writer->WritePacket(&packet_buffer_[0], 300, self_address_,
447 peer_address_, &options));
448 EXPECT_TRUE(writer->buffered_writes().empty());
449
450 // Pretend 1ms has elapsed and the 6th packet has 1ms less delay. In other
451 // words, the release time should still be the same as packets 3-5.
452 writer->ForceReleaseTimeMs(2);
453 options.release_time_delay = QuicTime::Delta::FromMilliseconds(4);
454 result = WritePacketWithOptions(writer.get(), &options);
455 ASSERT_EQ(write_buffered, result);
456 EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time);
457 EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
458 }
459
460 } // namespace
461 } // namespace test
462 } // namespace quic
463