1 /** @file
2 *
3 * A brief file description
4 *
5 * @section license License
6 *
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24 #include <tscore/Diags.h>
25 #include <QUICCongestionController.h>
26 #include <QUICNewRenoCongestionController.h>
27
28 #define QUICCCDebug(fmt, ...) \
29 Debug("quic_cc", \
30 "[%s] " \
31 "window:%" PRIu32 " in-flight:%" PRIu32 " ssthresh:%" PRIu32 " extra:%" PRIu32 " " fmt, \
32 this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \
33 this->_extra_packets_count, ##__VA_ARGS__)
34 #define QUICCCVDebug(fmt, ...) \
35 Debug("v_quic_cc", \
36 "[%s] " \
37 "window:%" PRIu32 " in-flight:%" PRIu32 " ssthresh:%" PRIu32 " extra:%" PRIu32 " " fmt, \
38 this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \
39 this->_extra_packets_count, ##__VA_ARGS__)
40
41 #define QUICCCError(fmt, ...) \
42 Error("quic_cc", \
43 "[%s] " \
44 "window:%" PRIu32 " in-flight:%" PRIu32 " ssthresh:%" PRIu32 " extra:%" PRIu32 " " fmt, \
45 this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \
46 this->_extra_packets_count, ##__VA_ARGS__)
47
QUICNewRenoCongestionController(QUICContext & context)48 QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICContext &context)
49 : _cc_mutex(new_ProxyMutex()), _context(context)
50 {
51 auto &cc_config = context.cc_config();
52 this->_k_initial_window = cc_config.initial_window();
53 this->_k_minimum_window = cc_config.minimum_window();
54 this->_k_loss_reduction_factor = cc_config.loss_reduction_factor();
55 this->_k_persistent_congestion_threshold = cc_config.persistent_congestion_threshold();
56
57 this->reset();
58 }
59
60 void
on_packet_sent(size_t bytes_sent)61 QUICNewRenoCongestionController::on_packet_sent(size_t bytes_sent)
62 {
63 SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
64 if (this->_extra_packets_count > 0) {
65 --this->_extra_packets_count;
66 }
67
68 this->_bytes_in_flight += bytes_sent;
69 }
70
71 bool
_in_congestion_recovery(ink_hrtime sent_time) const72 QUICNewRenoCongestionController::_in_congestion_recovery(ink_hrtime sent_time) const
73 {
74 return sent_time <= this->_congestion_recovery_start_time;
75 }
76
77 bool
_is_app_or_flow_control_limited()78 QUICNewRenoCongestionController::_is_app_or_flow_control_limited()
79 {
80 // FIXME : don't known how does app worked here
81 return false;
82 }
83
84 void
_maybe_send_one_packet()85 QUICNewRenoCongestionController::_maybe_send_one_packet()
86 {
87 // TODO Implement _maybe_send_one_packet
88 }
89
90 bool
_are_all_packets_lost(const std::map<QUICPacketNumber,QUICSentPacketInfoUPtr> & lost_packets,const QUICSentPacketInfoUPtr & largest_lost_packet,ink_hrtime period) const91 QUICNewRenoCongestionController::_are_all_packets_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &lost_packets,
92 const QUICSentPacketInfoUPtr &largest_lost_packet, ink_hrtime period) const
93 {
94 // check whether packets are continuous. return true if all continuous packets are in period
95 QUICPacketNumber next_expected = UINT64_MAX;
96 for (auto &it : lost_packets) {
97 if (it.second->time_sent >= largest_lost_packet->time_sent - period) {
98 if (next_expected == UINT64_MAX) {
99 next_expected = it.second->packet_number + 1;
100 continue;
101 }
102
103 if (next_expected != it.second->packet_number) {
104 return false;
105 }
106
107 next_expected = it.second->packet_number + 1;
108 }
109 }
110
111 return next_expected == UINT64_MAX ? false : true;
112 }
113
114 void
_congestion_event(ink_hrtime sent_time)115 QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time)
116 {
117 // Start a new congestion event if packet was sent after the
118 // start of the previous congestion recovery period.
119 if (!this->_in_congestion_recovery(sent_time)) {
120 this->_congestion_recovery_start_time = Thread::get_hrtime();
121 this->_congestion_window *= this->_k_loss_reduction_factor;
122 this->_congestion_window = std::max(this->_congestion_window, this->_k_minimum_window);
123 this->_ssthresh = this->_congestion_window;
124 this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::RECOVERY);
125 this->_context.trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_window, this->_bytes_in_flight,
126 this->_ssthresh);
127 // A packet can be sent to speed up loss recovery.
128 this->_maybe_send_one_packet();
129 }
130 }
131
132 void
process_ecn(const QUICAckFrame & ack_frame,QUICPacketNumberSpace pn_space,ink_hrtime largest_acked_time_sent)133 QUICNewRenoCongestionController::process_ecn(const QUICAckFrame &ack_frame, QUICPacketNumberSpace pn_space,
134 ink_hrtime largest_acked_time_sent)
135 {
136 // If the ECN-CE counter reported by the peer has increased,
137 // this could be a new congestion event.
138 if (ack_frame.ecn_section()->ecn_ce_count() > this->_ecn_ce_counters[static_cast<int>(pn_space)]) {
139 this->_ecn_ce_counters[static_cast<int>(pn_space)] = ack_frame.ecn_section()->ecn_ce_count();
140 // Start a new congestion event if the last acknowledged
141 // packet was sent after the start of the previous
142 // recovery epoch.
143 this->_congestion_event(largest_acked_time_sent);
144 }
145 }
146
147 bool
_in_persistent_congestion(const std::map<QUICPacketNumber,QUICSentPacketInfoUPtr> & lost_packets,const QUICSentPacketInfoUPtr & largest_lost_packet)148 QUICNewRenoCongestionController::_in_persistent_congestion(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &lost_packets,
149 const QUICSentPacketInfoUPtr &largest_lost_packet)
150 {
151 ink_hrtime congestion_period = this->_context.rtt_provider()->congestion_period(this->_k_persistent_congestion_threshold);
152 // Determine if all packets in the time period before the
153 // largest newly lost packet, including the edges, are
154 // marked lost
155 return this->_are_all_packets_lost(lost_packets, largest_lost_packet, congestion_period);
156 }
157
158 void
on_packets_acked(const std::vector<QUICSentPacketInfoUPtr> & packets)159 QUICNewRenoCongestionController::on_packets_acked(const std::vector<QUICSentPacketInfoUPtr> &packets)
160 {
161 SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
162
163 for (auto &packet : packets) {
164 // Remove from bytes_in_flight.
165 this->_bytes_in_flight -= packet->sent_bytes;
166 if (this->_in_congestion_recovery(packet->time_sent)) {
167 // Do not increase congestion window in recovery period.
168 continue;
169 }
170 if (this->_is_app_or_flow_control_limited()) {
171 // Do not increase congestion_window if application
172 // limited or flow control limited.
173 continue;
174 }
175 if (this->_congestion_window < this->_ssthresh) {
176 // Slow start.
177 this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::SLOW_START);
178 this->_congestion_window += packet->sent_bytes;
179 QUICCCVDebug("slow start window changed");
180 continue;
181 }
182 // Congestion avoidance.
183 this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED,
184 QUICCongestionController::State::CONGESTION_AVOIDANCE);
185 this->_congestion_window += this->_max_datagram_size * static_cast<double>(packet->sent_bytes) / this->_congestion_window;
186 QUICCCVDebug("Congestion avoidance window changed");
187 }
188 }
189
190 // additional code
191 // the original one is:
192 // OnPacketsLost(lost_packets):
193 void
on_packets_lost(const std::map<QUICPacketNumber,QUICSentPacketInfoUPtr> & lost_packets)194 QUICNewRenoCongestionController::on_packets_lost(const std::map<QUICPacketNumber, QUICSentPacketInfoUPtr> &lost_packets)
195 {
196 SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
197
198 // Remove lost packets from bytes_in_flight.
199 for (auto &lost_packet : lost_packets) {
200 this->_bytes_in_flight -= lost_packet.second->sent_bytes;
201 }
202 const auto &largest_lost_packet = lost_packets.rbegin()->second;
203 this->_congestion_event(largest_lost_packet->time_sent);
204
205 // Collapse congestion window if persistent congestion
206 if (this->_in_persistent_congestion(lost_packets, largest_lost_packet)) {
207 this->_congestion_window = this->_k_minimum_window;
208 }
209 }
210
211 void
on_packet_number_space_discarded(size_t bytes_in_flight)212 QUICNewRenoCongestionController::on_packet_number_space_discarded(size_t bytes_in_flight)
213 {
214 this->_bytes_in_flight -= bytes_in_flight;
215 }
216
217 bool
_check_credit() const218 QUICNewRenoCongestionController::_check_credit() const
219 {
220 if (this->_bytes_in_flight >= this->_congestion_window) {
221 QUICCCDebug("Congestion control pending");
222 }
223
224 return this->_bytes_in_flight < this->_congestion_window;
225 }
226
227 uint32_t
credit() const228 QUICNewRenoCongestionController::credit() const
229 {
230 if (this->_extra_packets_count) {
231 return UINT32_MAX;
232 }
233
234 if (this->_check_credit()) {
235 return this->_congestion_window - this->_bytes_in_flight;
236 } else {
237 return 0;
238 }
239 }
240
241 uint32_t
bytes_in_flight() const242 QUICNewRenoCongestionController::bytes_in_flight() const
243 {
244 return this->_bytes_in_flight;
245 }
246
247 uint32_t
congestion_window() const248 QUICNewRenoCongestionController::congestion_window() const
249 {
250 return this->_congestion_window;
251 }
252
253 uint32_t
current_ssthresh() const254 QUICNewRenoCongestionController::current_ssthresh() const
255 {
256 return this->_ssthresh;
257 }
258
259 // [draft-17 recovery] 7.9.3. Initialization
260 void
reset()261 QUICNewRenoCongestionController::reset()
262 {
263 SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread());
264
265 this->_congestion_window = this->_k_initial_window;
266 this->_bytes_in_flight = 0;
267 this->_congestion_recovery_start_time = 0;
268 this->_ssthresh = UINT32_MAX;
269 for (int i = 0; i < QUIC_N_PACKET_SPACES; ++i) {
270 this->_ecn_ce_counters[i] = 0;
271 }
272 }
273
274 void
add_extra_credit()275 QUICNewRenoCongestionController::add_extra_credit()
276 {
277 ++this->_extra_packets_count;
278 }
279