TAS
TCP Acceleration as an OS Service
cc.c
1 /*
2  * Copyright 2019 University of Washington, Max Planck Institute for
3  * Software Systems, and The University of Texas at Austin
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <utils.h>
29 
30 #include <tas.h>
31 #include "internal.h"
32 
33 #define CONF_MSS 1400
34 
35 int cc_init(void)
36 {
37  return 0;
38 }
39 
40 static inline void issue_retransmits(struct connection *c,
41  struct nicif_connection_stats *stats, uint32_t cur_ts);
42 
43 static inline void dctcp_win_init(struct connection *c);
44 static inline void dctcp_win_update(struct connection *c,
45  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts);
46 
47 static inline void dctcp_rate_init(struct connection *c);
48 static inline void dctcp_rate_update(struct connection *c,
49  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts);
50 
51 static inline void timely_init(struct connection *c);
52 static inline void timely_update(struct connection *c,
53  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts);
54 
55 static inline void const_rate_init(struct connection *c);
56 static inline void const_rate_update(struct connection *c,
57  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts);
58 
59 static inline uint32_t window_to_rate(uint32_t window, uint32_t rtt);
60 
61 static uint32_t last_ts = 0;
62 static struct connection *cc_conns = NULL;
63 static struct connection *next_conn = NULL;
64 
65 uint32_t cc_next_ts(uint32_t cur_ts)
66 {
67  struct connection *c;
68  assert(cur_ts >= last_ts);
69  uint32_t ts = -1U;
70 
71  for (c = cc_conns; c != NULL; c = c->cc_next) {
72  if (c->status != CONN_OPEN)
73  continue;
74 
75  int32_t next_ts = (c->cc_rtt * config.cc_control_interval) - (cur_ts - c->cc_last_ts);
76  if(next_ts >= 0) {
77  ts = MIN(ts, next_ts);
78  } else {
79  ts = 0;
80  }
81  }
82 
83  return (ts == -1U ? -1U : MAX(ts, config.cc_control_granularity - (cur_ts - last_ts)));
84 }
85 
86 unsigned cc_poll(uint32_t cur_ts)
87 {
88  struct connection *c, *c_first;
89  struct nicif_connection_stats stats;
90  uint32_t diff_ts;
91  uint32_t last;
92  unsigned n = 0;
93 
94  diff_ts = cur_ts - last_ts;
95  if (0 && diff_ts < config.cc_control_granularity)
96  return 0;
97 
98  c = c_first = (next_conn != NULL ? next_conn : cc_conns);
99  if (c == NULL) {
100  last_ts = cur_ts;
101  return 0;
102  }
103 
104  for (; n < 128 && (n == 0 || c != c_first);
105  c = (c->cc_next != NULL ? c->cc_next : cc_conns), n++)
106  {
107  if (c->status != CONN_OPEN)
108  continue;
109 
110  if (cur_ts - c->cc_last_ts < c->cc_rtt * config.cc_control_interval)
111  continue;
112 
113  if (nicif_connection_stats(c->flow_id, &stats)) {
114  fprintf(stderr, "cc_poll: nicif_connection_stats failed unexpectedly\n");
115  abort();
116  }
117 
118  /* calculate difference to last time */
119  last = c->cc_last_drops;
120  c->cc_last_drops = stats.c_drops;
121  stats.c_drops -= last;
122 
123  last = c->cc_last_acks;
124  c->cc_last_acks = stats.c_acks;
125  stats.c_acks -= last;
126 
127  last = c->cc_last_ackb;
128  c->cc_last_ackb = stats.c_ackb;
129  stats.c_ackb -= last;
130 
131  last = c->cc_last_ecnb;
132  c->cc_last_ecnb = stats.c_ecnb;
133  stats.c_ecnb -= last;
134 
135  kstats.drops += stats.c_drops;
136  kstats.ecn_marked += stats.c_ecnb;
137  kstats.acks += stats.c_ackb;
138 
139  switch (config.cc_algorithm) {
140  case CONFIG_CC_DCTCP_WIN:
141  dctcp_win_update(c, &stats, diff_ts, cur_ts);
142  break;
143 
144  case CONFIG_CC_DCTCP_RATE:
145  dctcp_rate_update(c, &stats, diff_ts, cur_ts);
146  break;
147 
148  case CONFIG_CC_TIMELY:
149  timely_update(c, &stats, diff_ts, cur_ts);
150  break;
151 
152  case CONFIG_CC_CONST_RATE:
153  const_rate_update(c, &stats, diff_ts, cur_ts);
154  break;
155 
156  default:
157  fprintf(stderr, "cc_poll: unknown CC algorithm (%u)\n",
158  config.cc_algorithm);
159  abort();
160  break;
161  }
162 
163  issue_retransmits(c, &stats, cur_ts);
164  nicif_connection_setrate(c->flow_id, c->cc_rate);
165 
166  c->cc_last_ts = cur_ts;
167 
168  }
169 
170  next_conn = c;
171  last_ts = cur_ts;
172  return n;
173 }
174 
175 void cc_conn_init(struct connection *conn)
176 {
177  conn->cc_next = cc_conns;
178  cc_conns = conn;
179 
180  conn->cc_last_ts = cur_ts;
181  conn->cc_rtt = config.tcp_rtt_init;
182  conn->cc_rexmits = 0;
183 
184  switch (config.cc_algorithm) {
185  case CONFIG_CC_DCTCP_WIN:
186  dctcp_win_init(conn);
187  break;
188 
189  case CONFIG_CC_DCTCP_RATE:
190  dctcp_rate_init(conn);
191  break;
192 
193  case CONFIG_CC_TIMELY:
194  timely_init(conn);
195  break;
196 
197  case CONFIG_CC_CONST_RATE:
198  const_rate_init(conn);
199  break;
200 
201  default:
202  fprintf(stderr, "cc_conn_init: unknown CC algorithm (%u)\n",
203  config.cc_algorithm);
204  abort();
205  break;
206  }
207 }
208 
209 void cc_conn_remove(struct connection *conn)
210 {
211  struct connection *cp = NULL;
212 
213  if (next_conn == conn) {
214  next_conn = conn->cc_next;
215  }
216 
217  if (cc_conns == conn) {
218  cc_conns = conn->cc_next;
219  } else {
220  for (cp = cc_conns; cp != NULL && cp->cc_next != conn;
221  cp = cp->cc_next);
222  if (cp == NULL) {
223  fprintf(stderr, "conn_unregister: connection not found\n");
224  abort();
225  }
226 
227  cp->cc_next = conn->cc_next;
228  }
229 }
230 
231 static inline void issue_retransmits(struct connection *c,
232  struct nicif_connection_stats *stats, uint32_t cur_ts)
233 {
234  uint32_t rtt = (stats->rtt != 0 ? stats->rtt : config.tcp_rtt_init);
235 
236  /* check for re-transmits */
237  if (stats->txp && stats->c_ackb == 0) {
238  if (c->cnt_tx_pending++ == 0) {
239  c->ts_tx_pending = cur_ts;
240  } else if (c->cnt_tx_pending >= config.cc_rexmit_ints &&
241  (cur_ts - c->ts_tx_pending) >= 2 * rtt)
242  {
244  c->cnt_tx_pending = 0;
245  kstats.kernel_rexmit++;
246  c->cc_rexmits++;
247  }
248  }
249  } else {
250  c->cnt_tx_pending = 0;
251  }
252 }
253 
254 /******************************************************************************/
255 /* Window-based DCTCP */
256 
257 static inline void dctcp_win_init(struct connection *c)
258 {
259  struct connection_cc_dctcp_win *cc = &c->cc.dctcp_win;
260 
261  cc->window = 2 * CONF_MSS;
262  c->cc_rate = window_to_rate(cc->window, config.tcp_rtt_init);
263  cc->ecn_rate = 0;
264  cc->slowstart = 1;
265 }
266 
267 static inline void dctcp_win_update(struct connection *c,
268  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts)
269 {
270  struct connection_cc_dctcp_win *cc = &c->cc.dctcp_win;
271  uint64_t ecn_rate, incr;
272  uint32_t rtt = stats->rtt, win = cc->window;
273 
274  assert(win >= CONF_MSS);
275 
276  /* If RTT is zero, use estimate */
277  if (rtt == 0) {
278  rtt = config.tcp_rtt_init;
279  }
280 
281  /* Slow start */
282  if (cc->slowstart) {
283  if (stats->c_drops == 0 && stats->c_ecnb == 0 && c->cc_rexmits == 0) {
284  /* double window, but ensure we don't overflow it */
285  if (win + stats->c_ackb > win)
286  win += stats->c_ackb;
287  } else {
288  /* if we see any indication of congestion go into congestion avoidance */
289  cc->slowstart = 0;
290  }
291  }
292 
293  /* Congestion avoidance */
294  if (!cc->slowstart) {
295  /* if we have drops, cut by half */
296  if (stats->c_drops > 0 || c->cc_rexmits > 0) {
297  win /= 2;
298  } else {
299  /* update ECN rate */
300  if (stats->c_ackb > 0) {
301  stats->c_ecnb = (stats->c_ecnb <= stats->c_ackb ? stats->c_ecnb :
302  stats->c_ackb);
303  ecn_rate = (((uint64_t) stats->c_ecnb) * UINT32_MAX) / stats->c_ackb;
304 
305  /* EWMA */
306  ecn_rate = ((ecn_rate * config.cc_dctcp_weight) +
307  ((uint64_t) cc->ecn_rate *
308  (UINT32_MAX - config.cc_dctcp_weight)));
309  ecn_rate /= UINT32_MAX;
310  cc->ecn_rate = ecn_rate;
311  }
312 
313  /* if ecn marks: reduce window */
314  if (stats->c_ecnb > 0) {
315  win = (((uint64_t) win) * (UINT32_MAX - cc->ecn_rate / 2)) / UINT32_MAX;
316  } else {
317  /* additive increase */
318  assert(win != 0);
319  incr = ((uint64_t) stats->c_ackb * CONF_MSS) / win;
320  if ((uint32_t) (win + incr) > win)
321  win += incr;
322  }
323  }
324  }
325 
326  /* Ensure window is at least 1 mss */
327  if (win < CONF_MSS)
328  win = CONF_MSS;
329 
330  /* A window larger than the send buffer also does not make much sense */
331  if (win > c->tx_len)
332  win = c->tx_len;
333 
334  c->cc_rtt = rtt;
335  c->cc_rate = window_to_rate(win, rtt);
336  assert(win >= CONF_MSS);
337  cc->window = win;
338  c->cc_rexmits = 0;
339 }
340 
342 static inline uint32_t window_to_rate(uint32_t window, uint32_t rtt)
343 {
344  uint64_t time, rate;
345 
346  /* calculate how long [ns] it will take to send a window size's worth */
347  time = (((uint64_t) window * 8 * 1000) / config.tcp_link_bw) / 1000;
348 
349  /* we won't be able to send more than a window per rtt */
350  if (time < rtt * 1000)
351  time = rtt * 1000;
352 
353  /* convert time to rate */
354  assert(time != 0);
355  rate = ((uint64_t) window * 8 * 1000000) / time;
356  if (rate > UINT32_MAX)
357  rate = UINT32_MAX;
358 
359  return rate;
360 }
361 
362 /******************************************************************************/
363 /* Rate-based DCTCP */
364 
365 static inline void dctcp_rate_init(struct connection *c)
366 {
367  struct connection_cc_dctcp_rate *cc = &c->cc.dctcp_rate;
368 
369  c->cc_rate = config.cc_dctcp_init;
370  cc->ecn_rate = 0;
371  cc->slowstart = 1;
372 
373  cc->unproc_ecnb = 0;
374  cc->unproc_acks = 0;
375  cc->unproc_ackb = 0;
376  cc->unproc_drops = 0;
377 
378 }
379 
380 static inline void dctcp_rate_update(struct connection *c,
381  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts)
382 {
383  struct connection_cc_dctcp_rate *cc = &c->cc.dctcp_rate;
384  uint64_t ecn_rate;
385  uint32_t act_rate, rate = c->cc_rate, rtt = stats->rtt, c_ecnb, c_acks,
386  c_ackb, c_drops;
387 
388  /* If RTT is zero, use estimate */
389  if (rtt == 0) {
390  rtt = config.tcp_rtt_init;
391  }
392  c->cc_rtt = rtt;
393 
394  c_ecnb = cc->unproc_ecnb + stats->c_ecnb;
395  c_acks = cc->unproc_acks + stats->c_acks;
396  c_ackb = cc->unproc_ackb + stats->c_ackb;
397  c_drops = cc->unproc_drops + stats->c_drops;
398 
399  if (c_acks < config.cc_dctcp_minpkts) {
400  cc->unproc_ecnb = c_ecnb;
401  cc->unproc_acks = c_acks;
402  cc->unproc_ackb = c_ackb;
403  cc->unproc_drops = c_drops;
404  return;
405  } else {
406  cc->unproc_ecnb = 0;
407  cc->unproc_acks = 0;
408  cc->unproc_ackb = 0;
409  cc->unproc_drops = 0;
410  }
411 
412  /* calculate actual rate */
413  if (c->cc_last_ts != 0) {
414  act_rate = c_ackb * 8 * 1000 / (cur_ts - c->cc_last_ts);
415  } else {
416  act_rate = 0;
417  }
418  cc->act_rate = (7 * cc->act_rate + act_rate) / 8;
419  act_rate = (act_rate >= cc->act_rate ? act_rate : cc->act_rate);
420 
421  /* clamp rate to actually used rate * 1.2 */
422  if (rate > (uint64_t) act_rate * 12 / 10) {
423  rate = (uint64_t) act_rate * 12 / 10;
424  }
425 
426  /* Slow start */
427  if (cc->slowstart) {
428  if (c_drops == 0 && c_ecnb == 0 && c->cc_rexmits == 0) {
429  /* double rate*/
430  if (rate * 2 >= rate)
431  rate *= 2;
432  else
433  rate = UINT32_MAX;
434  } else {
435  /* if we see any indication of congestion go into congestion avoidance */
436  cc->slowstart = 0;
437  }
438  }
439 
440  /* Congestion avoidance */
441  if (!cc->slowstart) {
442  /* if we have drops, cut by half */
443  if (c_drops > 0 || c->cc_rexmits > 0) {
444  rate /= 2;
445  } else {
446  /* update ECN rate */
447  if (c_ackb > 0) {
448  c_ecnb = (c_ecnb <= c_ackb ? c_ecnb : c_ackb);
449  ecn_rate = (((uint64_t) c_ecnb) * UINT32_MAX) / c_ackb;
450 
451  /* EWMA */
452  ecn_rate = ((ecn_rate * config.cc_dctcp_weight) +
453  ((uint64_t) cc->ecn_rate *
454  (UINT32_MAX - config.cc_dctcp_weight)));
455  ecn_rate /= UINT32_MAX;
456  cc->ecn_rate = ecn_rate;
457  }
458 
459  /* if ecn marks: reduce window */
460  if (c_ecnb > 0) {
461  rate = (((uint64_t) rate) * (UINT32_MAX - cc->ecn_rate / 2)) /
462  UINT32_MAX;
463  } else if (config.cc_dctcp_mimd == 0) {
464  /* additive increase */
465  rate += config.cc_dctcp_step;
466  } else {
467  /* multiplicative increase */
468  rate += (((uint64_t) rate) * config.cc_dctcp_mimd) / UINT32_MAX;
469  }
470  }
471  }
472 
473  /* ensure we're at least at the minimum rate */
474  if (rate < config.cc_dctcp_min)
475  rate = config.cc_dctcp_min;
476 
477  c->cc_rate = rate;
478  c->cc_rexmits = 0;
479 }
480 
481 /******************************************************************************/
482 /* TIMELY */
483 
484 static inline void timely_init(struct connection *c)
485 {
486  struct connection_cc_timely *cc = &c->cc.timely;
487  c->cc_rate = config.cc_timely_init;
488  cc->rtt_prev = cc->rtt_diff = cc->hai_cnt = 0;
489  cc->last_ts = 0;
490  cc->slowstart = 1;
491 }
492 
493 static inline void timely_update(struct connection *c,
494  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts)
495 {
496  struct connection_cc_timely *cc = &c->cc.timely;
497  int32_t new_rtt_diff = 0;
498  uint32_t new_rtt, new_rate, act_rate;
499  uint64_t factor;
500  int64_t x, normalized_gradient = 0;
501 
502  new_rtt = stats->rtt;
503 
504  /* calculate actual rate */
505  if (cc->last_ts != 0) {
506  act_rate = stats->c_ackb * 8 * 1000 / (cur_ts - cc->last_ts);
507  } else {
508  act_rate = 0;
509  }
510  cc->act_rate = (7 * cc->act_rate + act_rate) / 8;
511  act_rate = (act_rate >= cc->act_rate ? act_rate : cc->act_rate);
512 
513  /* no rtt estimate yet, a bit weird */
514  if (new_rtt == 0)
515  return;
516 
517  /* if in slow-start and rtt is above Thigh, exit slow-start */
518  if (cc->slowstart && new_rtt > (config.cc_timely_tlow + config.cc_timely_thigh) / 2) {
519  cc->slowstart = 0;
520  }
521 
522  /* clamp rate to actually used rate * 1.2 */
523  if (!cc->slowstart && c->cc_rate > (uint64_t) act_rate * 12 / 10) {
524  c->cc_rate = (uint64_t) act_rate * 12 / 10;
525  }
526 
527  /* can only calculate a gradient if we have a previous rtt */
528  if (cc->rtt_prev != 0) {
529  new_rtt_diff = new_rtt - cc->rtt_prev;
530 
531  /* calculate rtt_diff */
532  factor = config.cc_timely_alpha / 2;
533  x = (INT32_MAX - factor) * cc->rtt_diff + factor * new_rtt_diff;
534  cc->rtt_diff = x / INT32_MAX;
535 
536  /* calculate normalized_gradient */
537  normalized_gradient =
538  (int64_t) cc->rtt_diff * INT16_MAX / config.cc_timely_min_rtt;
539  }
540  cc->rtt_prev = new_rtt;
541 
542 
543  uint32_t orig_rate = c->cc_rate;
544  if (cc->slowstart) {
545  c->cc_rate *= 2;
546  } else if (new_rtt < config.cc_timely_tlow) {
547  new_rate = c->cc_rate;
548  new_rate += config.cc_timely_step;
549  c->cc_rate = new_rate;
550  cc->hai_cnt = 0;
551 
552  } else if (new_rtt > config.cc_timely_thigh) {
553  /* rate *= 1 - beta * (1 - Thigh/rtt)
554  * = 1 - a, a = beta * b, b = 1 - d, d = Thigh/rtt */
555 
556  uint32_t d = ((uint64_t) UINT32_MAX * config.cc_timely_thigh) / new_rtt;
557  uint32_t b = UINT32_MAX - d;
558  uint32_t a = (((uint64_t) config.cc_timely_beta) * b) / UINT32_MAX;
559  c->cc_rate = (((uint64_t) c->cc_rate) * (UINT32_MAX - a)) / UINT32_MAX;
560  cc->hai_cnt = 0;
561  } else if (normalized_gradient <= 0) {
562  if (++cc->hai_cnt >= 5) {
563  c->cc_rate += config.cc_timely_step * 5;
564  cc->hai_cnt--;
565  } else {
566  c->cc_rate += config.cc_timely_step;
567  }
568  } else {
569  /* rate *= 1 - beta * (normalized_gradient)
570  * = 1 - a, a = beta * normalized_gradient */
571 
572  int64_t a = ((int64_t) (config.cc_timely_beta / 2)) * normalized_gradient;
573  int64_t b = a / INT16_MAX;
574  int64_t d = (b <= INT32_MAX ? INT32_MAX - b : 0);
575  int64_t e = ((int64_t) (uint64_t) c->cc_rate) * d;
576  int64_t f = e / INT32_MAX;
577 
578  c->cc_rate = f;
579 
580  cc->hai_cnt = 0;
581  }
582 
583  if (c->cc_rate < orig_rate / 2) {
584  c->cc_rate = orig_rate / 2;
585  }
586 
587  if (c->cc_rate < config.cc_timely_min_rate)
588  c->cc_rate = config.cc_timely_min_rate;
589 
590  c->cc_rtt = stats->rtt;
591  cc->last_ts = cur_ts;
592  c->cc_rexmits = 0;
593 }
594 
595 /******************************************************************************/
596 /* Constant rate */
597 
598 static inline void const_rate_init(struct connection *c)
599 {
600  c->cc_rate = config.cc_const_rate;
601 }
602 
603 static inline void const_rate_update(struct connection *c,
604  struct nicif_connection_stats *stats, uint32_t diff_ts, uint32_t cur_ts)
605 {
606  c->cc_rtt = (stats->rtt != 0 ? stats->rtt : config.tcp_rtt_init);
607  c->cc_rexmits = 0;
608 }
uint32_t cc_timely_alpha
Definition: config.h:107
uint32_t cc_timely_thigh
Definition: config.h:101
int nicif_connection_setrate(uint32_t f_id, uint32_t rate)
Definition: nicif.c:314
uint64_t ecn_marked
Definition: internal.h:65
uint32_t cc_timely_tlow
Definition: config.h:99
uint32_t cc_dctcp_weight
Definition: config.h:85
uint32_t cc_timely_beta
Definition: config.h:109
uint32_t cc_dctcp_min
Definition: config.h:93
unsigned cc_poll(uint32_t cur_ts)
Definition: cc.c:86
uint32_t tcp_rtt_init
Definition: config.h:59
uint64_t acks
Definition: internal.h:67
uint32_t cc_control_granularity
Definition: config.h:79
union connection::@1 cc
uint64_t kernel_rexmit
Definition: internal.h:63
uint32_t flow_id
Definition: internal.h:538
uint64_t drops
Definition: internal.h:61
uint32_t cc_control_interval
Definition: config.h:81
enum connection_status status
Definition: internal.h:472
uint32_t cc_dctcp_mimd
Definition: config.h:91
uint32_t cc_dctcp_step
Definition: config.h:89
uint32_t cc_timely_min_rate
Definition: config.h:113
uint32_t cc_dctcp_minpkts
Definition: config.h:95
uint32_t cc_rtt
Definition: internal.h:502
struct connection_cc_dctcp_win dctcp_win
Definition: internal.h:519
uint32_t cc_last_ts
Definition: internal.h:500
uint32_t cc_dctcp_init
Definition: config.h:87
uint16_t flow_group
Definition: internal.h:544
int cc_init(void)
Definition: cc.c:35
int nicif_connection_retransmit(uint32_t f_id, uint16_t core)
Definition: nicif.c:330
uint32_t cc_timely_step
Definition: config.h:103
int nicif_connection_stats(uint32_t f_id, struct nicif_connection_stats *p_stats)
Definition: nicif.c:285
struct connection_cc_dctcp_rate dctcp_rate
Definition: internal.h:523
struct connection_cc_timely timely
Definition: internal.h:521
uint32_t cc_rexmit_ints
Definition: config.h:83
uint32_t cnt_tx_pending
Definition: internal.h:526
uint32_t ts_tx_pending
Definition: internal.h:528
void cc_conn_init(struct connection *conn)
Definition: cc.c:175
uint32_t cc_rexmits
Definition: internal.h:515
struct connection * cc_next
Definition: internal.h:530
uint32_t tx_len
Definition: internal.h:448
uint32_t cc_timely_min_rtt
Definition: config.h:111
void cc_conn_remove(struct connection *conn)
Definition: cc.c:209
uint32_t cc_const_rate
Definition: config.h:97
uint32_t cc_rate
Definition: internal.h:513
enum config_cc_algorithm cc_algorithm
Definition: config.h:77
uint32_t cc_timely_init
Definition: config.h:105
uint32_t tcp_link_bw
Definition: config.h:61