blob: 1a5fa4e5b1b984d0a0796b4a99906f18440c5ce8 [file] [log] [blame]
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +02001#include <haproxy/quic_cc.h>
Frédéric Lécaille8f991942023-03-24 15:14:45 +01002#include <haproxy/ticks.h>
3#include <haproxy/trace.h>
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +02004
5/* This source file is highly inspired from Linux kernel source file
6 * implementation for TCP Cubic. In fact, we have no choice if we do
7 * not want to use any floating point operations to be fast!
8 * (See net/ipv4/tcp_cubic.c)
9 */
10#define TRACE_SOURCE &trace_quic
11
12#define CUBIC_BETA_SCALE 1024
13#define CUBIC_BETA_SCALE_SHIFT 10
14/* beta = 0.7 ; C = 0.4 */
15#define CUBIC_BETA 717 /* CUBIC_BETA / CUBIC_BETA_SCALE = 0.7 */
16#define CUBIC_C 410 /* CUBIC_C / CUBIC_BETA_SCALE = 0.4 */
17
18#define CUBIC_BETA_SCALE_FACTOR_SHIFT (3 * CUBIC_BETA_SCALE_SHIFT)
19#define TIME_SCALE_FACTOR_SHIFT 10
20
21/* The maximum value which may be cubed an multiplied by CUBIC_BETA */
22#define CUBIC_DIFF_TIME_LIMIT 355535ULL /* ms */
23
24/* K cube factor: (1 - beta) / c */
25struct cubic {
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +020026 uint32_t state;
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +020027 uint32_t ssthresh;
28 uint32_t remaining_inc;
29 uint32_t remaining_tcp_inc;
30 uint32_t epoch_start;
31 uint32_t origin_point;
32 uint32_t K;
33 uint32_t last_w_max;
34 uint32_t tcp_wnd;
35 uint32_t recovery_start_time;
36};
37
38static void quic_cc_cubic_reset(struct quic_cc *cc)
39{
40 struct cubic *c = quic_cc_priv(cc);
41
Frédéric Lécaillede2ba862023-04-02 10:49:35 +020042 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +020043 c->state = QUIC_CC_ST_SS;
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +020044 c->ssthresh = QUIC_CC_INFINITE_SSTHESH;
45 c->remaining_inc = 0;
46 c->remaining_tcp_inc = 0;
47 c->epoch_start = 0;
48 c->origin_point = 0;
49 c->K = 0;
50 c->last_w_max = 0;
51 c->tcp_wnd = 0;
52 c->recovery_start_time = 0;
Frédéric Lécaillede2ba862023-04-02 10:49:35 +020053 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +020054}
55
56static int quic_cc_cubic_init(struct quic_cc *cc)
57{
58 quic_cc_cubic_reset(cc);
59 return 1;
60}
61
62/* Cubic root.
63 * Highly inspired from Linux kernel sources.
64 * See net/ipv4/tcp_cubic.c
65 */
66static uint32_t cubic_root(uint64_t val)
67{
68 uint32_t x, b, shift;
69
70 static const uint8_t v[] = {
71 0, 54, 54, 54, 118, 118, 118, 118,
72 123, 129, 134, 138, 143, 147, 151, 156,
73 157, 161, 164, 168, 170, 173, 176, 179,
74 181, 185, 187, 190, 192, 194, 197, 199,
75 200, 202, 204, 206, 209, 211, 213, 215,
76 217, 219, 221, 222, 224, 225, 227, 229,
77 231, 232, 234, 236, 237, 239, 240, 242,
78 244, 245, 246, 248, 250, 251, 252, 254,
79 };
80
Frédéric Lécaille2c77a5e2022-08-03 12:49:30 +020081 if (!val || (b = my_flsl(val)) < 7) {
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +020082 /* val in [0..63] */
83 return ((uint32_t)v[(uint32_t)val] + 35) >> 6;
84 }
85
86 b = ((b * 84) >> 8) - 1;
87 shift = (val >> (b * 3));
88
89 x = ((uint32_t)(((uint32_t)v[shift] + 10) << b)) >> 6;
90
91 x = 2 * x + (uint32_t)(val / ((uint64_t)x * (uint64_t)(x - 1)));
92 x = ((x * 341) >> 10);
93
94 return x;
95}
96
97static inline void quic_cubic_update(struct quic_cc *cc, uint32_t acked)
98{
99 struct cubic *c = quic_cc_priv(cc);
100 struct quic_path *path = container_of(cc, struct quic_path, cc);
101 /* Current cwnd as number of packets */
102 uint32_t t, target, inc, inc_diff;
103 uint64_t delta, diff;
104
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200105 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200106 if (!c->epoch_start) {
107 c->epoch_start = now_ms;
108 if (c->last_w_max <= path->cwnd) {
109 c->K = 0;
110 c->origin_point = path->cwnd;
111 }
112 else {
113 /* K = cubic_root((1 - beta) * W_max / C) */
114 c->K = cubic_root((c->last_w_max - path->cwnd) *
115 (CUBIC_BETA_SCALE - CUBIC_BETA) / CUBIC_C / path->mtu) << TIME_SCALE_FACTOR_SHIFT;
116 c->origin_point = c->last_w_max;
117 }
118
119 c->tcp_wnd = path->cwnd;
120 c->remaining_inc = 0;
121 c->remaining_tcp_inc = 0;
122 }
123
124 t = now_ms + path->loss.rtt_min - c->epoch_start;
125 if (t < c->K) {
126 diff = c->K - t;
127 }
128 else {
129 diff = t - c->K;
130 }
131
132 if (diff > CUBIC_DIFF_TIME_LIMIT) {
133 /* TODO : should not happen if we handle the case
134 * of very late acks receipt. This must be handled as a congestion
135 * control event: a very late ack should trigger a congestion
136 * control algorithm reset.
137 */
138 quic_cc_cubic_reset(cc);
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200139 goto leave;
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200140 }
141
142 delta = path->mtu * ((CUBIC_C * diff * diff * diff) >> (10 + 3 * TIME_SCALE_FACTOR_SHIFT));
143 if (t < c->K)
144 target = c->origin_point - delta;
145 else
146 target = c->origin_point + delta;
147
148 if (target > path->cwnd) {
149 inc_diff = c->remaining_inc + path->mtu * (target - path->cwnd);
150 c->remaining_inc = inc_diff % path->cwnd;
151 inc = inc_diff / path->cwnd;
152 }
153 else {
154 /* small increment */
155 inc_diff = c->remaining_inc + path->mtu;
156 c->remaining_inc = inc_diff % (100 * path->cwnd);
157 inc = inc_diff / (100 * path->cwnd);
158 }
159
160 inc_diff = c->remaining_tcp_inc + path->mtu * acked;
161 c->tcp_wnd += inc_diff / path->cwnd;
162 c->remaining_tcp_inc = inc_diff % path->cwnd;
163 /* TCP friendliness */
164 if (c->tcp_wnd > path->cwnd) {
165 uint32_t tcp_inc = path->mtu * (c->tcp_wnd - path->cwnd) / path->cwnd;
166 if (tcp_inc > inc)
167 inc = tcp_inc;
168 }
169
170 path->cwnd += inc;
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200171 leave:
172 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200173}
174
175static void quic_cc_cubic_slow_start(struct quic_cc *cc)
176{
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200177 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200178 quic_cc_cubic_reset(cc);
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200179 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200180}
181
182static void quic_enter_recovery(struct quic_cc *cc)
183{
184 struct quic_path *path = container_of(cc, struct quic_path, cc);
185 struct cubic *c = quic_cc_priv(cc);
186 /* Current cwnd as number of packets */
187
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200188 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200189 c->epoch_start = 0;
190 c->recovery_start_time = now_ms;
191 /* Fast convergence */
192 if (path->cwnd < c->last_w_max) {
193 /* (1 + beta) * path->cwnd / 2 */
194 c->last_w_max = (path->cwnd * (CUBIC_BETA_SCALE + CUBIC_BETA) / 2) >> CUBIC_BETA_SCALE_SHIFT;
195 }
196 else {
197 c->last_w_max = path->cwnd;
198 }
199 path->cwnd = (CUBIC_BETA * path->cwnd) >> CUBIC_BETA_SCALE_SHIFT;
200 c->ssthresh = QUIC_MAX(path->cwnd, path->min_cwnd);
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +0200201 c->state = QUIC_CC_ST_RP;
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200202 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc, NULL, cc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200203}
204
205/* Congestion slow-start callback. */
206static void quic_cc_cubic_ss_cb(struct quic_cc *cc, struct quic_cc_event *ev)
207{
208 struct quic_path *path = container_of(cc, struct quic_path, cc);
209 struct cubic *c = quic_cc_priv(cc);
210
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100211 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc);
212 TRACE_PROTO("CC cubic", QUIC_EV_CONN_CC, cc->qc, ev);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200213 switch (ev->type) {
214 case QUIC_CC_EVT_ACK:
Frédéric Lécailledb548472023-04-02 10:07:48 +0200215 if (path->cwnd < QUIC_CC_INFINITE_SSTHESH - ev->ack.acked)
216 path->cwnd += ev->ack.acked;
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200217 /* Exit to congestion avoidance if slow start threshold is reached. */
218 if (path->cwnd >= c->ssthresh)
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +0200219 c->state = QUIC_CC_ST_CA;
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200220 break;
221
222 case QUIC_CC_EVT_LOSS:
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200223 quic_enter_recovery(cc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200224 break;
225
226 case QUIC_CC_EVT_ECN_CE:
227 /* TODO */
228 break;
229 }
230
231 out:
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100232 TRACE_PROTO("CC cubic", QUIC_EV_CONN_CC, cc->qc, NULL, cc);
233 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200234}
235
236/* Congestion avoidance callback. */
237static void quic_cc_cubic_ca_cb(struct quic_cc *cc, struct quic_cc_event *ev)
238{
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100239 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc);
240 TRACE_PROTO("CC cubic", QUIC_EV_CONN_CC, cc->qc, ev);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200241 switch (ev->type) {
242 case QUIC_CC_EVT_ACK:
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200243 quic_cubic_update(cc, ev->ack.acked);
244 break;
245 case QUIC_CC_EVT_LOSS:
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200246 quic_enter_recovery(cc);
247 break;
248 case QUIC_CC_EVT_ECN_CE:
249 /* TODO */
250 break;
251 }
252
253 out:
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100254 TRACE_PROTO("CC cubic", QUIC_EV_CONN_CC, cc->qc, NULL, cc);
Frédéric Lécaille23b8eef2023-03-31 20:18:44 +0200255 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200256}
257
Frédéric Lécailled7243312023-03-21 11:54:02 +0100258/* Recovery period callback */
259static void quic_cc_cubic_rp_cb(struct quic_cc *cc, struct quic_cc_event *ev)
260{
261 struct cubic *c = quic_cc_priv(cc);
262
263 TRACE_ENTER(QUIC_EV_CONN_CC, cc->qc, ev);
Frédéric Lécaillede2ba862023-04-02 10:49:35 +0200264 TRACE_PROTO("CC cubic", QUIC_EV_CONN_CC, cc->qc, ev, cc);
Frédéric Lécailled7243312023-03-21 11:54:02 +0100265
Frédéric Lécailled7243312023-03-21 11:54:02 +0100266 switch (ev->type) {
267 case QUIC_CC_EVT_ACK:
268 /* RFC 9022 7.3.2. Recovery
269 * A recovery period ends and the sender enters congestion avoidance when a
270 * packet sent during the recovery period is acknowledged.
271 */
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100272 if (tick_is_le(ev->ack.time_sent, c->recovery_start_time)) {
273 TRACE_PROTO("CC cubic (still in recov. period)", QUIC_EV_CONN_CC, cc->qc);
Frédéric Lécailled7243312023-03-21 11:54:02 +0100274 goto leave;
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100275 }
Frédéric Lécailled7243312023-03-21 11:54:02 +0100276
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +0200277 c->state = QUIC_CC_ST_CA;
Frédéric Lécailled7243312023-03-21 11:54:02 +0100278 c->recovery_start_time = TICK_ETERNITY;
279 break;
280 case QUIC_CC_EVT_LOSS:
281 break;
282 case QUIC_CC_EVT_ECN_CE:
283 /* TODO */
284 break;
285 }
286
287 leave:
Frédéric Lécaille8f991942023-03-24 15:14:45 +0100288 TRACE_PROTO("CC cubic", QUIC_EV_CONN_CC, cc->qc, NULL, cc);
Frédéric Lécailled7243312023-03-21 11:54:02 +0100289 TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc, NULL, cc);
290}
291
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200292static void (*quic_cc_cubic_state_cbs[])(struct quic_cc *cc,
293 struct quic_cc_event *ev) = {
294 [QUIC_CC_ST_SS] = quic_cc_cubic_ss_cb,
295 [QUIC_CC_ST_CA] = quic_cc_cubic_ca_cb,
Frédéric Lécailled7243312023-03-21 11:54:02 +0100296 [QUIC_CC_ST_RP] = quic_cc_cubic_rp_cb,
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200297};
298
299static void quic_cc_cubic_event(struct quic_cc *cc, struct quic_cc_event *ev)
300{
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +0200301 struct cubic *c = quic_cc_priv(cc);
302
303 return quic_cc_cubic_state_cbs[c->state](cc, ev);
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200304}
305
306static void quic_cc_cubic_state_trace(struct buffer *buf, const struct quic_cc *cc)
307{
Frédéric Lécaille01314b82023-03-24 14:09:55 +0100308 struct quic_path *path;
309 struct cubic *c = quic_cc_priv(cc);
310
311 path = container_of(cc, struct quic_path, cc);
312 chunk_appendf(buf, " state=%s cwnd=%llu ssthresh=%d rpst=%dms",
Frédéric Lécaille7d6270a2023-04-02 12:43:22 +0200313 quic_cc_state_str(c->state),
Frédéric Lécaille01314b82023-03-24 14:09:55 +0100314 (unsigned long long)path->cwnd,
315 (int)c->ssthresh,
316 !tick_isset(c->recovery_start_time) ? -1 :
317 TICKS_TO_MS(tick_remain(c->recovery_start_time, now_ms)));
Frédéric Lécaille1c9c2f62022-06-01 15:06:58 +0200318}
319
320struct quic_cc_algo quic_cc_algo_cubic = {
321 .type = QUIC_CC_ALGO_TP_CUBIC,
322 .init = quic_cc_cubic_init,
323 .event = quic_cc_cubic_event,
324 .slow_start = quic_cc_cubic_slow_start,
325 .state_trace = quic_cc_cubic_state_trace,
326};