OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "net/quic/quic_flow_controller.h" | |
6 | |
7 #include "base/strings/stringprintf.h" | |
8 #include "net/quic/quic_bug_tracker.h" | |
9 #include "net/quic/quic_connection.h" | |
10 #include "net/quic/quic_flags.h" | |
11 #include "net/quic/quic_protocol.h" | |
12 | |
13 namespace net { | |
14 | |
15 #define ENDPOINT \ | |
16 (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") | |
17 | |
18 QuicFlowController::QuicFlowController(QuicConnection* connection, | |
19 QuicStreamId id, | |
20 Perspective perspective, | |
21 QuicStreamOffset send_window_offset, | |
22 QuicStreamOffset receive_window_offset, | |
23 bool should_auto_tune_receive_window) | |
24 : connection_(connection), | |
25 id_(id), | |
26 perspective_(perspective), | |
27 bytes_sent_(0), | |
28 send_window_offset_(send_window_offset), | |
29 bytes_consumed_(0), | |
30 highest_received_byte_offset_(0), | |
31 receive_window_offset_(receive_window_offset), | |
32 receive_window_size_(receive_window_offset), | |
33 auto_tune_receive_window_(should_auto_tune_receive_window), | |
34 last_blocked_send_window_offset_(0), | |
35 prev_window_update_time_(QuicTime::Zero()) { | |
36 receive_window_size_limit_ = (id_ == kConnectionLevelId) | |
37 ? kSessionReceiveWindowLimit | |
38 : kStreamReceiveWindowLimit; | |
39 | |
40 DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_ | |
41 << ", setting initial receive window offset to: " | |
42 << receive_window_offset_ | |
43 << ", max receive window to: " << receive_window_size_ | |
44 << ", max receive window limit to: " << receive_window_size_limit_ | |
45 << ", setting send window offset to: " << send_window_offset_; | |
46 } | |
47 | |
48 void QuicFlowController::AddBytesConsumed(QuicByteCount bytes_consumed) { | |
49 bytes_consumed_ += bytes_consumed; | |
50 DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_; | |
51 | |
52 MaybeSendWindowUpdate(); | |
53 } | |
54 | |
55 bool QuicFlowController::UpdateHighestReceivedOffset( | |
56 QuicStreamOffset new_offset) { | |
57 // Only update if offset has increased. | |
58 if (new_offset <= highest_received_byte_offset_) { | |
59 return false; | |
60 } | |
61 | |
62 DVLOG(1) << ENDPOINT << "Stream " << id_ | |
63 << " highest byte offset increased from: " | |
64 << highest_received_byte_offset_ << " to " << new_offset; | |
65 highest_received_byte_offset_ = new_offset; | |
66 return true; | |
67 } | |
68 | |
69 void QuicFlowController::AddBytesSent(QuicByteCount bytes_sent) { | |
70 if (bytes_sent_ + bytes_sent > send_window_offset_) { | |
71 QUIC_BUG << ENDPOINT << "Stream " << id_ << " Trying to send an extra " | |
72 << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_ | |
73 << ", and send_window_offset_ = " << send_window_offset_; | |
74 bytes_sent_ = send_window_offset_; | |
75 | |
76 // This is an error on our side, close the connection as soon as possible. | |
77 connection_->CloseConnection( | |
78 QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, | |
79 base::StringPrintf( | |
80 "%llu bytes over send window offset", | |
81 static_cast<unsigned long long>(send_window_offset_ - | |
82 (bytes_sent_ + bytes_sent))) | |
83 .c_str(), | |
84 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
85 return; | |
86 } | |
87 | |
88 bytes_sent_ += bytes_sent; | |
89 DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_; | |
90 } | |
91 | |
92 bool QuicFlowController::FlowControlViolation() { | |
93 if (highest_received_byte_offset_ > receive_window_offset_) { | |
94 LOG(ERROR) << ENDPOINT << "Flow control violation on stream " << id_ | |
95 << ", receive window offset: " << receive_window_offset_ | |
96 << ", highest received byte offset: " | |
97 << highest_received_byte_offset_; | |
98 return true; | |
99 } | |
100 return false; | |
101 } | |
102 | |
103 void QuicFlowController::MaybeIncreaseMaxWindowSize() { | |
104 // Core of receive window auto tuning. This method should be called before a | |
105 // WINDOW_UPDATE frame is sent. Ideally, window updates should occur close to | |
106 // once per RTT. If a window update happens much faster than RTT, it implies | |
107 // that the flow control window is imposing a bottleneck. To prevent this, | |
108 // this method will increase the receive window size (subject to a reasonable | |
109 // upper bound). For simplicity this algorithm is deliberately asymmetric, in | |
110 // that it may increase window size but never decreases. | |
111 | |
112 // Keep track of timing between successive window updates. | |
113 QuicTime now = connection_->clock()->ApproximateNow(); | |
114 QuicTime prev = prev_window_update_time_; | |
115 prev_window_update_time_ = now; | |
116 if (!prev.IsInitialized()) { | |
117 DVLOG(1) << ENDPOINT << "first window update for stream " << id_; | |
118 return; | |
119 } | |
120 | |
121 if (!auto_tune_receive_window_) { | |
122 return; | |
123 } | |
124 | |
125 // Get outbound RTT. | |
126 QuicTime::Delta rtt = | |
127 connection_->sent_packet_manager().GetRttStats()->smoothed_rtt(); | |
128 if (rtt.IsZero()) { | |
129 DVLOG(1) << ENDPOINT << "rtt zero for stream " << id_; | |
130 return; | |
131 } | |
132 | |
133 // Now we can compare timing of window updates with RTT. | |
134 QuicTime::Delta since_last = now - prev; | |
135 QuicTime::Delta two_rtt = 2 * rtt; | |
136 | |
137 if (since_last >= two_rtt) { | |
138 // If interval between window updates is sufficiently large, there | |
139 // is no need to increase receive_window_size_. | |
140 return; | |
141 } | |
142 | |
143 QuicByteCount old_window = receive_window_size_; | |
144 receive_window_size_ *= 2; | |
145 receive_window_size_ = | |
146 std::min(receive_window_size_, receive_window_size_limit_); | |
147 | |
148 if (receive_window_size_ > old_window) { | |
149 DVLOG(1) << ENDPOINT << "New max window increase for stream " << id_ | |
150 << " after " << since_last.ToMicroseconds() << " us, and RTT is " | |
151 << rtt.ToMicroseconds() | |
152 << "us. max wndw: " << receive_window_size_; | |
153 } else { | |
154 // TODO(ckrasic) - add a varz to track this (?). | |
155 DVLOG(1) << ENDPOINT << "Max window at limit for stream " << id_ | |
156 << " after " << since_last.ToMicroseconds() << " us, and RTT is " | |
157 << rtt.ToMicroseconds() | |
158 << "us. Limit size: " << receive_window_size_; | |
159 } | |
160 } | |
161 | |
162 QuicByteCount QuicFlowController::WindowUpdateThreshold() { | |
163 return receive_window_size_ / 2; | |
164 } | |
165 | |
166 void QuicFlowController::MaybeSendWindowUpdate() { | |
167 // Send WindowUpdate to increase receive window if | |
168 // (receive window offset - consumed bytes) < (max window / 2). | |
169 // This is behaviour copied from SPDY. | |
170 DCHECK_LE(bytes_consumed_, receive_window_offset_); | |
171 QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_; | |
172 QuicByteCount threshold = WindowUpdateThreshold(); | |
173 | |
174 if (available_window >= threshold) { | |
175 DVLOG(1) << ENDPOINT << "Not sending WindowUpdate for stream " << id_ | |
176 << ", available window: " << available_window | |
177 << " >= threshold: " << threshold; | |
178 return; | |
179 } | |
180 | |
181 MaybeIncreaseMaxWindowSize(); | |
182 | |
183 // Update our receive window. | |
184 receive_window_offset_ += (receive_window_size_ - available_window); | |
185 | |
186 DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_ | |
187 << ", consumed bytes: " << bytes_consumed_ | |
188 << ", available window: " << available_window | |
189 << ", and threshold: " << threshold | |
190 << ", and receive window size: " << receive_window_size_ | |
191 << ". New receive window offset is: " << receive_window_offset_; | |
192 | |
193 // Inform the peer of our new receive window. | |
194 connection_->SendWindowUpdate(id_, receive_window_offset_); | |
195 } | |
196 | |
197 void QuicFlowController::MaybeSendBlocked() { | |
198 if (SendWindowSize() == 0 && | |
199 last_blocked_send_window_offset_ < send_window_offset_) { | |
200 DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. " | |
201 << "Send window: " << SendWindowSize() | |
202 << ", bytes sent: " << bytes_sent_ | |
203 << ", send limit: " << send_window_offset_; | |
204 // The entire send_window has been consumed, we are now flow control | |
205 // blocked. | |
206 connection_->SendBlocked(id_); | |
207 | |
208 // Keep track of when we last sent a BLOCKED frame so that we only send one | |
209 // at a given send offset. | |
210 last_blocked_send_window_offset_ = send_window_offset_; | |
211 } | |
212 } | |
213 | |
214 bool QuicFlowController::UpdateSendWindowOffset( | |
215 QuicStreamOffset new_send_window_offset) { | |
216 // Only update if send window has increased. | |
217 if (new_send_window_offset <= send_window_offset_) { | |
218 return false; | |
219 } | |
220 | |
221 DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_ | |
222 << " with new offset " << new_send_window_offset | |
223 << " current offset: " << send_window_offset_ | |
224 << " bytes_sent: " << bytes_sent_; | |
225 | |
226 const bool blocked = IsBlocked(); | |
227 send_window_offset_ = new_send_window_offset; | |
228 return blocked; | |
229 } | |
230 | |
231 bool QuicFlowController::IsBlocked() const { | |
232 return SendWindowSize() == 0; | |
233 } | |
234 | |
235 uint64_t QuicFlowController::SendWindowSize() const { | |
236 if (bytes_sent_ > send_window_offset_) { | |
237 return 0; | |
238 } | |
239 return send_window_offset_ - bytes_sent_; | |
240 } | |
241 | |
242 void QuicFlowController::UpdateReceiveWindowSize(QuicStreamOffset size) { | |
243 DVLOG(1) << ENDPOINT << "UpdateReceiveWindowSize for stream " << id_ << ": " | |
244 << size; | |
245 if (receive_window_size_ != receive_window_offset_) { | |
246 QUIC_BUG << "receive_window_size_:" << receive_window_size_ | |
247 << " != receive_window_offset:" << receive_window_offset_; | |
248 return; | |
249 } | |
250 receive_window_size_ = size; | |
251 receive_window_offset_ = size; | |
252 } | |
253 | |
254 } // namespace net | |
OLD | NEW |