-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcongestion_rate.go
More file actions
346 lines (302 loc) · 13.5 KB
/
Copy pathcongestion_rate.go
File metadata and controls
346 lines (302 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
package mipstack
import "time"
// tcpDeliveryTimestamp is a wrapping microsecond timestamp. Every sampled interval is
// bounded by current flight or an idle restart, so modulo subtraction remains
// valid across its approximately 71-minute wrap interval.
type tcpDeliveryTimestamp uint32
// tcpDeliveryTimestampAt compacts a stack-relative nanosecond stamp.
func tcpDeliveryTimestampAt(stamp monotonicStamp) tcpDeliveryTimestamp {
if stamp == 0 {
return 0
}
value := tcpDeliveryTimestamp((uint64(stamp)-1)/uint64(time.Microsecond)) + 1
if value == 0 {
return 1
}
return value
}
// tcpDeliveryTimestampDuration returns a modulo-safe microsecond interval.
func tcpDeliveryTimestampDuration(later, earlier tcpDeliveryTimestamp) time.Duration {
if later == 0 || earlier == 0 {
return 0
}
delta := uint32(later - earlier)
if delta == 0 {
return 0
}
return time.Duration(delta) * time.Microsecond
}
const (
// tcpDeliveryApplicationLimited packs the application-limited flag into deliveredFlags.
tcpDeliveryApplicationLimited = uint32(1) << 31
// tcpDeliveryDeliveredMask extracts the wrapping delivered-byte counter.
tcpDeliveryDeliveredMask = tcpDeliveryApplicationLimited - 1
)
// tcpDeliverySnapshot is Linux tcp_rate_skb_sent's per-transmission delivery
// snapshot. Compact stamps and a packed flag keep it at 12 bytes.
type tcpDeliverySnapshot struct {
firstSent tcpDeliveryTimestamp
deliveredStamp tcpDeliveryTimestamp
deliveredFlags uint32
}
// delivered returns the byte counter without its packed flag.
func (s tcpDeliverySnapshot) delivered() uint32 { return s.deliveredFlags & tcpDeliveryDeliveredMask }
// applicationLimited reports the packed Linux app-limited snapshot bit.
func (s tcpDeliverySnapshot) applicationLimited() bool {
return s.deliveredFlags&tcpDeliveryApplicationLimited != 0
}
// tcpDeliveryAfterEqual compares the 31-bit delivery counter while its
// unambiguous half-range remains at least the maximum TCP flight size.
func tcpDeliveryAfterEqual(value, reference uint32) bool {
delta := (value - reference) & tcpDeliveryDeliveredMask
return delta == 0 || delta < tcpDeliveryApplicationLimited/2
}
// CongestionRateSample is one ACK's read-only delivery-rate observation.
// Its interval is the longer of the send and acknowledgement phases. Valid
// reports false when TCP could not form an unambiguous rate sample; ACK and
// loss accounting remain usable. Rates use bytes because mipstack's SACK
// scoreboard is byte exact.
//
// TCP owns this value. A controller may read it only while handling the event
// that supplied it and must not retain its pointer. Accessors deliberately
// hide transport-only sequence and timestamp selection metadata so future
// sampler changes do not alter the public congestion-control contract.
type CongestionRateSample struct {
priorDelivered uint32
priorDeliveredTotal uint64
delivered uint32
acked uint32
losses uint64
priorInFlight uint32
inFlight uint32
interval time.Duration
rtt time.Duration
smoothedRTT time.Duration
ackTime time.Time
ackStamp tcpDeliveryTimestamp
lastSent monotonicStamp
lastEnd uint32
applicationLimited bool
schedulerLimited bool
retransmitted bool
recovery bool
fastRecovery bool
ackDelayed bool
tailLossProbeACK bool
valid bool
packetState uint64
firstSent tcpDeliveryTimestamp
priorStamp tcpDeliveryTimestamp
}
// tcpDeliveryRateSample is the transport's name for its public read-only view.
// The alias lets the built-in algorithms share the sample without conversion.
type tcpDeliveryRateSample = CongestionRateSample
// PriorDeliveredBytes returns the cumulative delivered-byte count captured
// when the sampled range was transmitted.
func (s *CongestionRateSample) PriorDeliveredBytes() uint64 { return s.priorDeliveredTotal }
// DeliveredBytes returns bytes delivered since the sampled transmission's
// delivery snapshot.
func (s *CongestionRateSample) DeliveredBytes() uint32 { return s.delivered }
// AcknowledgedBytes returns the cumulative ACK byte advance.
func (s *CongestionRateSample) AcknowledgedBytes() uint32 { return s.acked }
// LostBytes returns the bytes newly proven lost by this sample.
func (s *CongestionRateSample) LostBytes() uint64 { return s.losses }
// PriorBytesInFlight returns flight immediately before ACK processing.
func (s *CongestionRateSample) PriorBytesInFlight() uint32 { return s.priorInFlight }
// BytesInFlight returns flight after ACK processing and transmissions caused
// by the same ACK.
func (s *CongestionRateSample) BytesInFlight() uint32 { return s.inFlight }
// Interval returns the rate interval selected from the send and ACK phases.
func (s *CongestionRateSample) Interval() time.Duration { return s.interval }
// RTT returns the selected range's round-trip time when unambiguous.
func (s *CongestionRateSample) RTT() time.Duration { return s.rtt }
// SmoothedRTT returns the current RFC 6298 smoothed round-trip time.
func (s *CongestionRateSample) SmoothedRTT() time.Duration { return s.smoothedRTT }
// ACKTime returns packet ingress time rather than later processing time.
func (s *CongestionRateSample) ACKTime() time.Time { return s.ackTime }
// ApplicationLimited reports a sender bubble in the sampled interval.
func (s *CongestionRateSample) ApplicationLimited() bool { return s.applicationLimited }
// SchedulerLimited reports material local scheduling delay in the interval.
func (s *CongestionRateSample) SchedulerLimited() bool { return s.schedulerLimited }
// Retransmitted reports ambiguous delivery through a retransmitted range.
func (s *CongestionRateSample) Retransmitted() bool { return s.retransmitted }
// InRecovery reports fast- or timeout-recovery processing for this ACK.
func (s *CongestionRateSample) InRecovery() bool { return s.recovery }
// InFastRecovery reports fast recovery specifically.
func (s *CongestionRateSample) InFastRecovery() bool { return s.fastRecovery }
// ACKDelayed reports a lone runt sample likely delayed by the receiver.
func (s *CongestionRateSample) ACKDelayed() bool { return s.ackDelayed }
// TailLossProbeACK reports that this ACK exactly covers a retransmitted
// tail-loss probe whose original and probe deliveries cannot yet be
// distinguished. Model-based controllers can use it to retain round-local
// delivery signals, matching Linux rate_sample.is_acking_tlp_retrans_seq.
func (s *CongestionRateSample) TailLossProbeACK() bool { return s.tailLossProbeACK }
// Valid reports whether DeliveredBytes divided by Interval is a usable rate.
func (s *CongestionRateSample) Valid() bool { return s.valid }
// PacketState returns the opaque state produced by the transmission event for
// the range selected to form this sample. It is zero when the controller did
// not request transmission events or the range predates a controller change.
func (s *CongestionRateSample) PacketState() uint64 { return s.packetState }
// tcpDeliveryRateEstimator owns Linux-style connection delivery accounting.
// Model-based controllers embed it so the TCP actor can build one common rate
// sample without depending on an algorithm's private path model.
type tcpDeliveryRateEstimator struct {
delivered uint64
deliveredStamp tcpDeliveryTimestamp
firstSent tcpDeliveryTimestamp
applicationLimitedUntil uint64
schedulerLimitedUntil uint64
schedulerLimitedEvents uint64
totalLost uint64
sampledLost uint64
}
// initializeDelivery seeds a new estimator at the connection's current
// monotonic timestamp.
func (d *tcpDeliveryRateEstimator) initializeDelivery(_ time.Time, _ time.Duration, stamp monotonicStamp) {
d.restartFlight(stamp)
}
// observe retains delivery metadata from the most recently transmitted range
// newly acknowledged by the cumulative ACK or SACK scoreboard.
func (s *tcpDeliveryRateSample) observe(segment sentTCPSegment) {
snapshot := segment.delivery
if snapshot.deliveredStamp == 0 {
return
}
sent := segment.hostQueue.queuedAt
if s.priorStamp != 0 && (sent < s.lastSent || sent == s.lastSent && !tcpSequenceGreater(segment.end, s.lastEnd)) {
return
}
s.priorDelivered = snapshot.delivered()
s.priorStamp = snapshot.deliveredStamp
s.firstSent = snapshot.firstSent
s.lastSent = sent
s.lastEnd = segment.end
s.applicationLimited = snapshot.applicationLimited()
s.schedulerLimited = segment.state.has(sentTCPSegmentDeliverySchedulerLimited)
s.retransmitted = segment.isRetransmitted()
s.packetState = segment.congestionPacketState
}
// finishRateSample advances delivery accounting and validates the sample using
// the longer of its send and ACK phases, as Linux tcp_rate_gen does.
func (d *tcpDeliveryRateEstimator) finishRateSample(sample *tcpDeliveryRateSample, acknowledged uint32, priorInFlight, inFlight uint32, now time.Time, nowStamp monotonicStamp, minimumRTT, smoothedRTT, sampleRTT time.Duration) {
sample.acked = acknowledged
sample.priorInFlight = priorInFlight
sample.inFlight = inFlight
sample.ackTime = now
sample.rtt = sampleRTT
sample.smoothedRTT = smoothedRTT
sample.losses = d.totalLost - d.sampledLost
d.sampledLost = d.totalLost
if acknowledged != 0 {
d.delivered += uint64(acknowledged)
d.deliveredStamp = tcpDeliveryTimestampAt(nowStamp)
}
sample.ackStamp = d.deliveredStamp
if d.applicationLimitedUntil != 0 && d.delivered > d.applicationLimitedUntil {
d.applicationLimitedUntil = 0
}
if d.schedulerLimitedUntil != 0 && d.delivered > d.schedulerLimitedUntil {
d.schedulerLimitedUntil = 0
}
if sample.priorStamp == 0 {
return
}
// tcp_rate_skb_delivered advances first_tx_mstamp to the transmit time of
// the newest range selected for this ACK. Future packets snapshot this new
// boundary so their send phase does not grow from the connection's first
// flight forever.
compactNow := tcpDeliveryTimestampAt(nowStamp)
compactSent := tcpDeliveryTimestampAt(sample.lastSent)
d.firstSent = compactSent
if !sample.retransmitted {
if selectedRTT := tcpDeliveryTimestampDuration(compactNow, compactSent); selectedRTT > 0 {
sample.rtt = selectedRTT
}
}
sample.delivered = (uint32(d.delivered) - sample.priorDelivered) & tcpDeliveryDeliveredMask
sample.priorDeliveredTotal = d.delivered - uint64(sample.delivered)
sendInterval := tcpDeliveryTimestampDuration(compactSent, sample.firstSent)
ackInterval := tcpDeliveryTimestampDuration(compactNow, sample.priorStamp)
if ackInterval > sendInterval {
sendInterval = ackInterval
}
if sendInterval <= 0 || minimumRTT > 0 && sendInterval < minimumRTT {
return
}
sample.interval = sendInterval
sample.valid = true
}
// noteLoss records bytes newly declared lost for the next rate sample.
func (d *tcpDeliveryRateEstimator) noteLoss(bytes uint32) {
d.totalLost += uint64(bytes)
}
// recordLoss records one loss event and prevents timer-consumed loss from
// being repeated by the next ACK-generated sample.
func (d *tcpDeliveryRateEstimator) recordLoss(bytes uint32, duringACK bool) {
d.noteLoss(bytes)
if !duringACK {
d.sampledLost = d.totalLost
}
}
// markApplicationLimited records the delivery boundary that drains a sender bubble.
func (d *tcpDeliveryRateEstimator) markApplicationLimited(flight uint32) {
limit := d.delivered + uint64(flight)
if limit == 0 {
limit = 1
}
d.applicationLimitedUntil = limit
}
// markSchedulerLimited records a delivery boundary whose samples include a
// material userspace scheduling delay rather than a path limitation.
func (d *tcpDeliveryRateEstimator) markSchedulerLimited(flight uint32) {
limit := d.delivered + uint64(flight)
if limit == 0 {
limit = 1
}
if limit > d.schedulerLimitedUntil {
d.schedulerLimitedUntil = limit
}
d.schedulerLimitedEvents++
}
// schedulerLimited reports whether new transmissions still belong to a
// scheduler-limited delivery interval.
func (d *tcpDeliveryRateEstimator) schedulerLimited() bool {
return d.schedulerLimitedUntil != 0
}
// restartFlight starts delivery timestamps for a newly nonempty flight.
func (d *tcpDeliveryRateEstimator) restartFlight(stamp monotonicStamp) {
compactStamp := tcpDeliveryTimestampAt(stamp)
d.firstSent = compactStamp
d.deliveredStamp = compactStamp
}
// snapshot captures the current delivery state for one transmitted range.
func (d *tcpDeliveryRateEstimator) snapshot() tcpDeliverySnapshot {
deliveredFlags := uint32(d.delivered) & tcpDeliveryDeliveredMask
if d.applicationLimitedUntil != 0 {
deliveredFlags |= tcpDeliveryApplicationLimited
}
return tcpDeliverySnapshot{firstSent: d.firstSent, deliveredStamp: d.deliveredStamp, deliveredFlags: deliveredFlags}
}
// onDeliveryDataSent returns the common snapshot for one original
// transmission. A newly nonempty flight restarts both pipeline timestamps.
func (d *tcpDeliveryRateEstimator) onDeliveryDataSent(_, _ int, _ time.Time, stamp monotonicStamp, packetsOut, window uint32) (tcpDeliverySnapshot, uint32) {
if packetsOut == 0 {
d.restartFlight(stamp)
}
return d.snapshot(), window
}
// onDeliveryRetransmit returns the common snapshot for a retransmitted range.
func (d *tcpDeliveryRateEstimator) onDeliveryRetransmit(_, _ int, _ time.Time, stamp monotonicStamp, packetsOut uint32) tcpDeliverySnapshot {
if packetsOut == 0 {
d.restartFlight(stamp)
}
return d.snapshot()
}
// snapshotSend captures common delivery state for a range whose snapshot must
// be refreshed after ACK processing sends more data.
func (d *tcpDeliveryRateEstimator) snapshotSend(stamp monotonicStamp, packetsOut uint32) tcpDeliverySnapshot {
if packetsOut == 0 {
d.restartFlight(stamp)
}
return d.snapshot()
}