xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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