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 <time.h>
8
9 #include <ctime>
10
11 #include "quiche/quic/core/quic_linux_socket_utils.h"
12 #include "quiche/quic/platform/api/quic_server_stats.h"
13
14 namespace quic {
15
16 // static
17 std::unique_ptr<QuicBatchWriterBuffer>
CreateBatchWriterBuffer()18 QuicGsoBatchWriter::CreateBatchWriterBuffer() {
19 return std::make_unique<QuicBatchWriterBuffer>();
20 }
21
QuicGsoBatchWriter(int fd)22 QuicGsoBatchWriter::QuicGsoBatchWriter(int fd)
23 : QuicGsoBatchWriter(fd, CLOCK_MONOTONIC) {}
24
QuicGsoBatchWriter(int fd,clockid_t clockid_for_release_time)25 QuicGsoBatchWriter::QuicGsoBatchWriter(int fd,
26 clockid_t clockid_for_release_time)
27 : QuicUdpBatchWriter(CreateBatchWriterBuffer(), fd),
28 clockid_for_release_time_(clockid_for_release_time),
29 supports_release_time_(
30 GetQuicRestartFlag(quic_support_release_time_for_gso) &&
31 QuicLinuxSocketUtils::EnableReleaseTime(fd,
32 clockid_for_release_time)) {
33 if (supports_release_time_) {
34 QUIC_RESTART_FLAG_COUNT(quic_support_release_time_for_gso);
35 }
36 }
37
QuicGsoBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer,int fd,clockid_t clockid_for_release_time,ReleaseTimeForceEnabler)38 QuicGsoBatchWriter::QuicGsoBatchWriter(
39 std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, int fd,
40 clockid_t clockid_for_release_time, ReleaseTimeForceEnabler /*enabler*/)
41 : QuicUdpBatchWriter(std::move(batch_buffer), fd),
42 clockid_for_release_time_(clockid_for_release_time),
43 supports_release_time_(true) {
44 QUIC_DLOG(INFO) << "Release time forcefully enabled.";
45 }
46
CanBatch(const char *,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,const PerPacketOptions *,const QuicPacketWriterParams & params,uint64_t release_time) const47 QuicGsoBatchWriter::CanBatchResult QuicGsoBatchWriter::CanBatch(
48 const char* /*buffer*/, size_t buf_len, const QuicIpAddress& self_address,
49 const QuicSocketAddress& peer_address, const PerPacketOptions* /*options*/,
50 const QuicPacketWriterParams& params, uint64_t release_time) const {
51 // If there is nothing buffered already, this write will be included in this
52 // batch.
53 if (buffered_writes().empty()) {
54 return CanBatchResult(/*can_batch=*/true, /*must_flush=*/false);
55 }
56
57 // The new write can be batched if all of the following are true:
58 // [0] The total number of the GSO segments(one write=one segment, including
59 // the new write) must not exceed |max_segments|.
60 // [1] It has the same source and destination addresses as already buffered
61 // writes.
62 // [2] It won't cause this batch to exceed kMaxGsoPacketSize.
63 // [3] Already buffered writes all have the same length.
64 // [4] Length of already buffered writes must >= length of the new write.
65 // [5] The ECN markings match.
66 // [6] The new packet can be released without delay, or it has the same
67 // release time as buffered writes.
68 const BufferedWrite& first = buffered_writes().front();
69 const BufferedWrite& last = buffered_writes().back();
70 // Whether this packet can be sent without delay, regardless of release time.
71 const bool can_burst = !SupportsReleaseTime() ||
72 params.release_time_delay.IsZero() ||
73 params.allow_burst;
74 size_t max_segments = MaxSegments(first.buf_len);
75 bool can_batch =
76 buffered_writes().size() < max_segments && // [0]
77 last.self_address == self_address && // [1]
78 last.peer_address == peer_address && // [1]
79 batch_buffer().SizeInUse() + buf_len <= kMaxGsoPacketSize && // [2]
80 first.buf_len == last.buf_len && // [3]
81 first.buf_len >= buf_len && // [4]
82 first.params.ecn_codepoint == params.ecn_codepoint && // [5]
83 (can_burst || first.release_time == release_time); // [6]
84
85 // A flush is required if any of the following is true:
86 // [a] The new write can't be batched.
87 // [b] Length of the new write is different from the length of already
88 // buffered writes.
89 // [c] The total number of the GSO segments, including the new write, reaches
90 // |max_segments|.
91 bool must_flush = (!can_batch) || // [a]
92 (last.buf_len != buf_len) || // [b]
93 (buffered_writes().size() + 1 == max_segments); // [c]
94 return CanBatchResult(can_batch, must_flush);
95 }
96
GetReleaseTime(const QuicPacketWriterParams & params) const97 QuicGsoBatchWriter::ReleaseTime QuicGsoBatchWriter::GetReleaseTime(
98 const QuicPacketWriterParams& params) const {
99 QUICHE_DCHECK(SupportsReleaseTime());
100
101 const uint64_t now = NowInNanosForReleaseTime();
102 const uint64_t ideal_release_time =
103 now + params.release_time_delay.ToMicroseconds() * 1000;
104
105 if ((params.release_time_delay.IsZero() || params.allow_burst) &&
106 !buffered_writes().empty() &&
107 // If release time of buffered packets is in the past, flush buffered
108 // packets and buffer this packet at the ideal release time.
109 (buffered_writes().back().release_time >= now)) {
110 // Send as soon as possible, but no sooner than the last buffered packet.
111 const uint64_t actual_release_time = buffered_writes().back().release_time;
112
113 const int64_t offset_ns = actual_release_time - ideal_release_time;
114 ReleaseTime result{actual_release_time,
115 QuicTime::Delta::FromMicroseconds(offset_ns / 1000)};
116
117 QUIC_DVLOG(1) << "ideal_release_time:" << ideal_release_time
118 << ", actual_release_time:" << actual_release_time
119 << ", offset:" << result.release_time_offset;
120 return result;
121 }
122
123 // Send according to the release time delay.
124 return {ideal_release_time, QuicTime::Delta::Zero()};
125 }
126
NowInNanosForReleaseTime() const127 uint64_t QuicGsoBatchWriter::NowInNanosForReleaseTime() const {
128 struct timespec ts;
129
130 if (clock_gettime(clockid_for_release_time_, &ts) != 0) {
131 return 0;
132 }
133
134 return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
135 }
136
137 // static
BuildCmsg(QuicMsgHdr * hdr,const QuicIpAddress & self_address,uint16_t gso_size,uint64_t release_time,QuicEcnCodepoint ecn_codepoint)138 void QuicGsoBatchWriter::BuildCmsg(QuicMsgHdr* hdr,
139 const QuicIpAddress& self_address,
140 uint16_t gso_size, uint64_t release_time,
141 QuicEcnCodepoint ecn_codepoint) {
142 hdr->SetIpInNextCmsg(self_address);
143 if (gso_size > 0) {
144 *hdr->GetNextCmsgData<uint16_t>(SOL_UDP, UDP_SEGMENT) = gso_size;
145 }
146 if (release_time != 0) {
147 *hdr->GetNextCmsgData<uint64_t>(SOL_SOCKET, SO_TXTIME) = release_time;
148 }
149 if (ecn_codepoint != ECN_NOT_ECT && GetQuicRestartFlag(quic_support_ect1)) {
150 QUIC_RESTART_FLAG_COUNT_N(quic_support_ect1, 8, 9);
151 if (self_address.IsIPv4()) {
152 *hdr->GetNextCmsgData<int>(IPPROTO_IP, IP_TOS) =
153 static_cast<int>(ecn_codepoint);
154 } else {
155 *hdr->GetNextCmsgData<int>(IPPROTO_IPV6, IPV6_TCLASS) =
156 static_cast<int>(ecn_codepoint);
157 }
158 }
159 }
160
FlushImpl()161 QuicGsoBatchWriter::FlushImplResult QuicGsoBatchWriter::FlushImpl() {
162 return InternalFlushImpl<kCmsgSpace>(BuildCmsg);
163 }
164
165 } // namespace quic
166