ngtcp2_cc.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * ngtcp2
  3. *
  4. * Copyright (c) 2018 ngtcp2 contributors
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining
  7. * a copy of this software and associated documentation files (the
  8. * "Software"), to deal in the Software without restriction, including
  9. * without limitation the rights to use, copy, modify, merge, publish,
  10. * distribute, sublicense, and/or sell copies of the Software, and to
  11. * permit persons to whom the Software is furnished to do so, subject to
  12. * the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  21. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  22. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  23. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24. */
  25. #include "ngtcp2_cc.h"
  26. #include <assert.h>
  27. #include <string.h>
  28. #include "ngtcp2_log.h"
  29. #include "ngtcp2_macro.h"
  30. #include "ngtcp2_mem.h"
  31. #include "ngtcp2_rcvry.h"
  32. #include "ngtcp2_conn_stat.h"
  33. #include "ngtcp2_rst.h"
  34. #include "ngtcp2_unreachable.h"
  35. uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
  36. uint64_t n = 2 * max_udp_payload_size;
  37. n = ngtcp2_max_uint64(n, 14720);
  38. return ngtcp2_min_uint64(10 * max_udp_payload_size, n);
  39. }
  40. ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
  41. size_t pktlen, ngtcp2_pktns_id pktns_id,
  42. ngtcp2_tstamp sent_ts, uint64_t lost,
  43. uint64_t tx_in_flight, int is_app_limited) {
  44. pkt->pkt_num = pkt_num;
  45. pkt->pktlen = pktlen;
  46. pkt->pktns_id = pktns_id;
  47. pkt->sent_ts = sent_ts;
  48. pkt->lost = lost;
  49. pkt->tx_in_flight = tx_in_flight;
  50. pkt->is_app_limited = is_app_limited;
  51. return pkt;
  52. }
  53. static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; }
  54. void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
  55. memset(reno, 0, sizeof(*reno));
  56. reno->cc.log = log;
  57. reno->cc.on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked;
  58. reno->cc.congestion_event = ngtcp2_cc_reno_cc_congestion_event;
  59. reno->cc.on_persistent_congestion =
  60. ngtcp2_cc_reno_cc_on_persistent_congestion;
  61. reno->cc.reset = ngtcp2_cc_reno_cc_reset;
  62. reno_cc_reset(reno);
  63. }
  64. static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
  65. ngtcp2_tstamp sent_time) {
  66. return cstat->congestion_recovery_start_ts != UINT64_MAX &&
  67. sent_time <= cstat->congestion_recovery_start_ts;
  68. }
  69. void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  70. const ngtcp2_cc_pkt *pkt,
  71. ngtcp2_tstamp ts) {
  72. ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
  73. uint64_t m;
  74. (void)ts;
  75. if (in_congestion_recovery(cstat, pkt->sent_ts) || pkt->is_app_limited) {
  76. return;
  77. }
  78. if (cstat->cwnd < cstat->ssthresh) {
  79. cstat->cwnd += pkt->pktlen;
  80. ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
  81. "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
  82. pkt->pkt_num, cstat->cwnd);
  83. return;
  84. }
  85. m = cstat->max_tx_udp_payload_size * pkt->pktlen + reno->pending_add;
  86. reno->pending_add = m % cstat->cwnd;
  87. cstat->cwnd += m / cstat->cwnd;
  88. }
  89. void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  90. ngtcp2_tstamp sent_ts,
  91. uint64_t bytes_lost, ngtcp2_tstamp ts) {
  92. ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
  93. uint64_t min_cwnd;
  94. (void)bytes_lost;
  95. if (in_congestion_recovery(cstat, sent_ts)) {
  96. return;
  97. }
  98. cstat->congestion_recovery_start_ts = ts;
  99. cstat->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS;
  100. min_cwnd = 2 * cstat->max_tx_udp_payload_size;
  101. cstat->cwnd = ngtcp2_max_uint64(cstat->cwnd, min_cwnd);
  102. cstat->ssthresh = cstat->cwnd;
  103. reno->pending_add = 0;
  104. ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
  105. "reduce cwnd because of packet loss cwnd=%" PRIu64,
  106. cstat->cwnd);
  107. }
  108. void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
  109. ngtcp2_conn_stat *cstat,
  110. ngtcp2_tstamp ts) {
  111. (void)cc;
  112. (void)ts;
  113. cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
  114. cstat->congestion_recovery_start_ts = UINT64_MAX;
  115. }
  116. void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  117. ngtcp2_tstamp ts) {
  118. ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
  119. (void)cstat;
  120. (void)ts;
  121. reno_cc_reset(reno);
  122. }
  123. static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
  124. v->cwnd_prior = 0;
  125. v->w_max = 0;
  126. v->k = 0;
  127. v->epoch_start = UINT64_MAX;
  128. v->w_est = 0;
  129. v->state = NGTCP2_CUBIC_STATE_INITIAL;
  130. v->app_limited_start_ts = UINT64_MAX;
  131. v->app_limited_duration = 0;
  132. v->pending_bytes_delivered = 0;
  133. v->pending_est_bytes_delivered = 0;
  134. }
  135. static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
  136. cubic_vars_reset(&cubic->current);
  137. cubic_vars_reset(&cubic->undo.v);
  138. cubic->undo.cwnd = 0;
  139. cubic->undo.ssthresh = 0;
  140. cubic->hs.current_round_min_rtt = UINT64_MAX;
  141. cubic->hs.last_round_min_rtt = UINT64_MAX;
  142. cubic->hs.curr_rtt = UINT64_MAX;
  143. cubic->hs.rtt_sample_count = 0;
  144. cubic->hs.css_baseline_min_rtt = UINT64_MAX;
  145. cubic->hs.css_round = 0;
  146. cubic->next_round_delivered = 0;
  147. }
  148. void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
  149. ngtcp2_rst *rst) {
  150. memset(cubic, 0, sizeof(*cubic));
  151. cubic->cc.log = log;
  152. cubic->cc.on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv;
  153. cubic->cc.congestion_event = ngtcp2_cc_cubic_cc_congestion_event;
  154. cubic->cc.on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion;
  155. cubic->cc.on_persistent_congestion =
  156. ngtcp2_cc_cubic_cc_on_persistent_congestion;
  157. cubic->cc.reset = ngtcp2_cc_cubic_cc_reset;
  158. cubic->rst = rst;
  159. cubic_cc_reset(cubic);
  160. }
  161. uint64_t ngtcp2_cbrt(uint64_t n) {
  162. size_t s;
  163. uint64_t y = 0;
  164. uint64_t b;
  165. for (s = 63; s > 0; s -= 3) {
  166. y <<= 1;
  167. b = 3 * y * (y + 1) + 1;
  168. if ((n >> s) >= b) {
  169. n -= b << s;
  170. y++;
  171. }
  172. }
  173. y <<= 1;
  174. b = 3 * y * (y + 1) + 1;
  175. if (n >= b) {
  176. n -= b;
  177. y++;
  178. }
  179. return y;
  180. }
  181. /* RFC 9406 HyStart++ constants */
  182. #define NGTCP2_HS_MIN_RTT_THRESH (4 * NGTCP2_MILLISECONDS)
  183. #define NGTCP2_HS_MAX_RTT_THRESH (16 * NGTCP2_MILLISECONDS)
  184. #define NGTCP2_HS_MIN_RTT_DIVISOR 8
  185. #define NGTCP2_HS_N_RTT_SAMPLE 8
  186. #define NGTCP2_HS_CSS_GROWTH_DIVISOR 4
  187. #define NGTCP2_HS_CSS_ROUNDS 5
  188. static uint64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic,
  189. const ngtcp2_conn_stat *cstat,
  190. ngtcp2_tstamp ts) {
  191. ngtcp2_duration t = ts - cubic->current.epoch_start;
  192. uint64_t delta;
  193. uint64_t tx = (t << 10) / NGTCP2_SECONDS;
  194. uint64_t kx = (cubic->current.k << 10) / NGTCP2_SECONDS;
  195. uint64_t time_delta;
  196. if (tx < kx) {
  197. return UINT64_MAX;
  198. }
  199. time_delta = tx - kx;
  200. delta = cstat->max_tx_udp_payload_size *
  201. ((((time_delta * time_delta) >> 10) * time_delta) >> 10) * 4 / 10;
  202. return cubic->current.w_max + (delta >> 10);
  203. }
  204. void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  205. const ngtcp2_cc_ack *ack,
  206. ngtcp2_tstamp ts) {
  207. ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
  208. uint64_t w_cubic, w_cubic_next, target, m;
  209. ngtcp2_duration rtt_thresh;
  210. int round_start;
  211. if (in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
  212. return;
  213. }
  214. if (cubic->current.state == NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE) {
  215. if (cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited) {
  216. if (cubic->current.app_limited_start_ts == UINT64_MAX) {
  217. cubic->current.app_limited_start_ts = ts;
  218. }
  219. return;
  220. }
  221. if (cubic->current.app_limited_start_ts != UINT64_MAX) {
  222. cubic->current.app_limited_duration +=
  223. ts - cubic->current.app_limited_start_ts;
  224. cubic->current.app_limited_start_ts = UINT64_MAX;
  225. }
  226. } else if (cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited) {
  227. return;
  228. }
  229. round_start = ack->pkt_delivered >= cubic->next_round_delivered;
  230. if (round_start) {
  231. cubic->next_round_delivered = cubic->rst->delivered;
  232. cubic->rst->is_cwnd_limited = 0;
  233. }
  234. if (cstat->cwnd < cstat->ssthresh) {
  235. /* slow-start */
  236. if (cubic->hs.css_round) {
  237. cstat->cwnd += ack->bytes_delivered / NGTCP2_HS_CSS_GROWTH_DIVISOR;
  238. } else {
  239. cstat->cwnd += ack->bytes_delivered;
  240. }
  241. ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
  242. "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64,
  243. ack->bytes_delivered, cstat->cwnd);
  244. if (round_start) {
  245. cubic->hs.last_round_min_rtt = cubic->hs.current_round_min_rtt;
  246. cubic->hs.current_round_min_rtt = UINT64_MAX;
  247. cubic->hs.rtt_sample_count = 0;
  248. if (cubic->hs.css_round) {
  249. ++cubic->hs.css_round;
  250. }
  251. }
  252. cubic->hs.current_round_min_rtt =
  253. ngtcp2_min_uint64(cubic->hs.current_round_min_rtt, ack->rtt);
  254. ++cubic->hs.rtt_sample_count;
  255. if (cubic->hs.css_round) {
  256. if (cubic->hs.current_round_min_rtt < cubic->hs.css_baseline_min_rtt) {
  257. cubic->hs.css_baseline_min_rtt = UINT64_MAX;
  258. cubic->hs.css_round = 0;
  259. return;
  260. }
  261. if (cubic->hs.css_round >= NGTCP2_HS_CSS_ROUNDS) {
  262. ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
  263. "HyStart++ exit slow start");
  264. cstat->ssthresh = cstat->cwnd;
  265. }
  266. return;
  267. }
  268. if (cubic->hs.rtt_sample_count >= NGTCP2_HS_N_RTT_SAMPLE &&
  269. cubic->hs.current_round_min_rtt != UINT64_MAX &&
  270. cubic->hs.last_round_min_rtt != UINT64_MAX) {
  271. rtt_thresh =
  272. ngtcp2_max_uint64(NGTCP2_HS_MIN_RTT_THRESH,
  273. ngtcp2_min_uint64(cubic->hs.last_round_min_rtt /
  274. NGTCP2_HS_MIN_RTT_DIVISOR,
  275. NGTCP2_HS_MAX_RTT_THRESH));
  276. if (cubic->hs.current_round_min_rtt >=
  277. cubic->hs.last_round_min_rtt + rtt_thresh) {
  278. cubic->hs.css_baseline_min_rtt = cubic->hs.current_round_min_rtt;
  279. cubic->hs.css_round = 1;
  280. }
  281. }
  282. return;
  283. }
  284. /* congestion avoidance */
  285. switch (cubic->current.state) {
  286. case NGTCP2_CUBIC_STATE_INITIAL:
  287. m = cstat->max_tx_udp_payload_size * ack->bytes_delivered +
  288. cubic->current.pending_bytes_delivered;
  289. cstat->cwnd += m / cstat->cwnd;
  290. cubic->current.pending_bytes_delivered = m % cstat->cwnd;
  291. return;
  292. case NGTCP2_CUBIC_STATE_RECOVERY:
  293. cubic->current.state = NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE;
  294. cubic->current.epoch_start = ts;
  295. break;
  296. default:
  297. break;
  298. }
  299. w_cubic = cubic_cc_compute_w_cubic(cubic, cstat,
  300. ts - cubic->current.app_limited_duration);
  301. w_cubic_next = cubic_cc_compute_w_cubic(
  302. cubic, cstat,
  303. ts - cubic->current.app_limited_duration + cstat->smoothed_rtt);
  304. if (w_cubic_next == UINT64_MAX || w_cubic_next < cstat->cwnd) {
  305. target = cstat->cwnd;
  306. } else if (2 * w_cubic_next > 3 * cstat->cwnd) {
  307. target = cstat->cwnd * 3 / 2;
  308. } else {
  309. target = w_cubic_next;
  310. }
  311. m = ack->bytes_delivered * cstat->max_tx_udp_payload_size +
  312. cubic->current.pending_est_bytes_delivered;
  313. cubic->current.pending_est_bytes_delivered = m % cstat->cwnd;
  314. if (cubic->current.w_est < cubic->current.cwnd_prior) {
  315. cubic->current.w_est += m * 9 / 17 / cstat->cwnd;
  316. } else {
  317. cubic->current.w_est += m / cstat->cwnd;
  318. }
  319. if (w_cubic == UINT64_MAX || cubic->current.w_est > w_cubic) {
  320. cstat->cwnd = cubic->current.w_est;
  321. } else {
  322. m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size +
  323. cubic->current.pending_bytes_delivered;
  324. cstat->cwnd += m / cstat->cwnd;
  325. cubic->current.pending_bytes_delivered = m % cstat->cwnd;
  326. }
  327. ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
  328. "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
  329. " k=%" PRIi64 " target=%" PRIu64 " w_est=%" PRIu64,
  330. ack->bytes_delivered, cstat->cwnd, cubic->current.k, target,
  331. cubic->current.w_est);
  332. }
  333. void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  334. ngtcp2_tstamp sent_ts,
  335. uint64_t bytes_lost,
  336. ngtcp2_tstamp ts) {
  337. ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
  338. uint64_t flight_size;
  339. if (in_congestion_recovery(cstat, sent_ts)) {
  340. return;
  341. }
  342. if (cubic->undo.cwnd < cstat->cwnd) {
  343. cubic->undo.v = cubic->current;
  344. cubic->undo.cwnd = cstat->cwnd;
  345. cubic->undo.ssthresh = cstat->ssthresh;
  346. }
  347. cstat->congestion_recovery_start_ts = ts;
  348. cubic->current.state = NGTCP2_CUBIC_STATE_RECOVERY;
  349. cubic->current.epoch_start = UINT64_MAX;
  350. cubic->current.app_limited_start_ts = UINT64_MAX;
  351. cubic->current.app_limited_duration = 0;
  352. cubic->current.pending_bytes_delivered = 0;
  353. cubic->current.pending_est_bytes_delivered = 0;
  354. if (cstat->cwnd < cubic->current.w_max) {
  355. cubic->current.w_max = cstat->cwnd * 17 / 20;
  356. } else {
  357. cubic->current.w_max = cstat->cwnd;
  358. }
  359. cstat->ssthresh = cstat->cwnd * 7 / 10;
  360. if (cubic->rst->rs.delivered * 2 < cstat->cwnd) {
  361. flight_size = cstat->bytes_in_flight + bytes_lost;
  362. cstat->ssthresh = ngtcp2_min_uint64(
  363. cstat->ssthresh,
  364. ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size) * 7 / 10);
  365. }
  366. cstat->ssthresh =
  367. ngtcp2_max_uint64(cstat->ssthresh, 2 * cstat->max_tx_udp_payload_size);
  368. cubic->current.cwnd_prior = cstat->cwnd;
  369. cstat->cwnd = cstat->ssthresh;
  370. cubic->current.w_est = cstat->cwnd;
  371. if (cstat->cwnd < cubic->current.w_max) {
  372. cubic->current.k =
  373. ngtcp2_cbrt(((cubic->current.w_max - cstat->cwnd) << 10) * 10 / 4 /
  374. cstat->max_tx_udp_payload_size) *
  375. NGTCP2_SECONDS;
  376. cubic->current.k >>= 10;
  377. } else {
  378. cubic->current.k = 0;
  379. }
  380. ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
  381. "reduce cwnd because of packet loss cwnd=%" PRIu64,
  382. cstat->cwnd);
  383. }
  384. void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc,
  385. ngtcp2_conn_stat *cstat,
  386. ngtcp2_tstamp ts) {
  387. ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
  388. (void)ts;
  389. cstat->congestion_recovery_start_ts = UINT64_MAX;
  390. if (cstat->cwnd < cubic->undo.cwnd) {
  391. cubic->current = cubic->undo.v;
  392. cstat->cwnd = cubic->undo.cwnd;
  393. cstat->ssthresh = cubic->undo.ssthresh;
  394. ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
  395. "spurious congestion is detected and congestion state is "
  396. "restored cwnd=%" PRIu64,
  397. cstat->cwnd);
  398. }
  399. cubic_vars_reset(&cubic->undo.v);
  400. cubic->undo.cwnd = 0;
  401. cubic->undo.ssthresh = 0;
  402. }
  403. void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
  404. ngtcp2_conn_stat *cstat,
  405. ngtcp2_tstamp ts) {
  406. ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
  407. (void)ts;
  408. cubic_cc_reset(cubic);
  409. cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
  410. cstat->congestion_recovery_start_ts = UINT64_MAX;
  411. }
  412. void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  413. ngtcp2_tstamp ts) {
  414. ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
  415. (void)cstat;
  416. (void)ts;
  417. cubic_cc_reset(cubic);
  418. }