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