cc_newreno.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #include "internal/quic_cc.h"
  2. #include "internal/quic_types.h"
  3. #include "internal/safe_math.h"
  4. OSSL_SAFE_MATH_UNSIGNED(u64, uint64_t)
  5. typedef struct ossl_cc_newreno_st {
  6. /* Dependencies. */
  7. OSSL_TIME (*now_cb)(void *arg);
  8. void *now_cb_arg;
  9. /* 'Constants' (which we allow to be configurable). */
  10. uint64_t k_init_wnd, k_min_wnd;
  11. uint32_t k_loss_reduction_factor_num, k_loss_reduction_factor_den;
  12. uint32_t persistent_cong_thresh;
  13. /* State. */
  14. size_t max_dgram_size;
  15. uint64_t bytes_in_flight, cong_wnd, slow_start_thresh, bytes_acked;
  16. OSSL_TIME cong_recovery_start_time;
  17. /* Unflushed state during multiple on-loss calls. */
  18. int processing_loss; /* 1 if not flushed */
  19. OSSL_TIME tx_time_of_last_loss;
  20. /* Diagnostic state. */
  21. int in_congestion_recovery;
  22. /* Diagnostic output locations. */
  23. size_t *p_diag_max_dgram_payload_len;
  24. uint64_t *p_diag_cur_cwnd_size;
  25. uint64_t *p_diag_min_cwnd_size;
  26. uint64_t *p_diag_cur_bytes_in_flight;
  27. uint32_t *p_diag_cur_state;
  28. } OSSL_CC_NEWRENO;
  29. #define MIN_MAX_INIT_WND_SIZE 14720 /* RFC 9002 s. 7.2 */
  30. /* TODO(QUIC FUTURE): Pacing support. */
  31. static void newreno_set_max_dgram_size(OSSL_CC_NEWRENO *nr,
  32. size_t max_dgram_size);
  33. static void newreno_update_diag(OSSL_CC_NEWRENO *nr);
  34. static void newreno_reset(OSSL_CC_DATA *cc);
  35. static OSSL_CC_DATA *newreno_new(OSSL_TIME (*now_cb)(void *arg),
  36. void *now_cb_arg)
  37. {
  38. OSSL_CC_NEWRENO *nr;
  39. if ((nr = OPENSSL_zalloc(sizeof(*nr))) == NULL)
  40. return NULL;
  41. nr->now_cb = now_cb;
  42. nr->now_cb_arg = now_cb_arg;
  43. newreno_set_max_dgram_size(nr, QUIC_MIN_INITIAL_DGRAM_LEN);
  44. newreno_reset((OSSL_CC_DATA *)nr);
  45. return (OSSL_CC_DATA *)nr;
  46. }
  47. static void newreno_free(OSSL_CC_DATA *cc)
  48. {
  49. OPENSSL_free(cc);
  50. }
  51. static void newreno_set_max_dgram_size(OSSL_CC_NEWRENO *nr,
  52. size_t max_dgram_size)
  53. {
  54. size_t max_init_wnd;
  55. int is_reduced = (max_dgram_size < nr->max_dgram_size);
  56. nr->max_dgram_size = max_dgram_size;
  57. max_init_wnd = 2 * max_dgram_size;
  58. if (max_init_wnd < MIN_MAX_INIT_WND_SIZE)
  59. max_init_wnd = MIN_MAX_INIT_WND_SIZE;
  60. nr->k_init_wnd = 10 * max_dgram_size;
  61. if (nr->k_init_wnd > max_init_wnd)
  62. nr->k_init_wnd = max_init_wnd;
  63. nr->k_min_wnd = 2 * max_dgram_size;
  64. if (is_reduced)
  65. nr->cong_wnd = nr->k_init_wnd;
  66. newreno_update_diag(nr);
  67. }
  68. static void newreno_reset(OSSL_CC_DATA *cc)
  69. {
  70. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  71. nr->k_loss_reduction_factor_num = 1;
  72. nr->k_loss_reduction_factor_den = 2;
  73. nr->persistent_cong_thresh = 3;
  74. nr->cong_wnd = nr->k_init_wnd;
  75. nr->bytes_in_flight = 0;
  76. nr->bytes_acked = 0;
  77. nr->slow_start_thresh = UINT64_MAX;
  78. nr->cong_recovery_start_time = ossl_time_zero();
  79. nr->processing_loss = 0;
  80. nr->tx_time_of_last_loss = ossl_time_zero();
  81. nr->in_congestion_recovery = 0;
  82. }
  83. static int newreno_set_input_params(OSSL_CC_DATA *cc, const OSSL_PARAM *params)
  84. {
  85. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  86. const OSSL_PARAM *p;
  87. size_t value;
  88. p = OSSL_PARAM_locate_const(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN);
  89. if (p != NULL) {
  90. if (!OSSL_PARAM_get_size_t(p, &value))
  91. return 0;
  92. if (value < QUIC_MIN_INITIAL_DGRAM_LEN)
  93. return 0;
  94. newreno_set_max_dgram_size(nr, value);
  95. }
  96. return 1;
  97. }
  98. static int bind_diag(OSSL_PARAM *params, const char *param_name, size_t len,
  99. void **pp)
  100. {
  101. const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_name);
  102. *pp = NULL;
  103. if (p == NULL)
  104. return 1;
  105. if (p->data_type != OSSL_PARAM_UNSIGNED_INTEGER
  106. || p->data_size != len)
  107. return 0;
  108. *pp = p->data;
  109. return 1;
  110. }
  111. static int newreno_bind_diagnostic(OSSL_CC_DATA *cc, OSSL_PARAM *params)
  112. {
  113. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  114. size_t *new_p_max_dgram_payload_len;
  115. uint64_t *new_p_cur_cwnd_size;
  116. uint64_t *new_p_min_cwnd_size;
  117. uint64_t *new_p_cur_bytes_in_flight;
  118. uint32_t *new_p_cur_state;
  119. if (!bind_diag(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN,
  120. sizeof(size_t), (void **)&new_p_max_dgram_payload_len)
  121. || !bind_diag(params, OSSL_CC_OPTION_CUR_CWND_SIZE,
  122. sizeof(uint64_t), (void **)&new_p_cur_cwnd_size)
  123. || !bind_diag(params, OSSL_CC_OPTION_MIN_CWND_SIZE,
  124. sizeof(uint64_t), (void **)&new_p_min_cwnd_size)
  125. || !bind_diag(params, OSSL_CC_OPTION_CUR_BYTES_IN_FLIGHT,
  126. sizeof(uint64_t), (void **)&new_p_cur_bytes_in_flight)
  127. || !bind_diag(params, OSSL_CC_OPTION_CUR_STATE,
  128. sizeof(uint32_t), (void **)&new_p_cur_state))
  129. return 0;
  130. if (new_p_max_dgram_payload_len != NULL)
  131. nr->p_diag_max_dgram_payload_len = new_p_max_dgram_payload_len;
  132. if (new_p_cur_cwnd_size != NULL)
  133. nr->p_diag_cur_cwnd_size = new_p_cur_cwnd_size;
  134. if (new_p_min_cwnd_size != NULL)
  135. nr->p_diag_min_cwnd_size = new_p_min_cwnd_size;
  136. if (new_p_cur_bytes_in_flight != NULL)
  137. nr->p_diag_cur_bytes_in_flight = new_p_cur_bytes_in_flight;
  138. if (new_p_cur_state != NULL)
  139. nr->p_diag_cur_state = new_p_cur_state;
  140. newreno_update_diag(nr);
  141. return 1;
  142. }
  143. static void unbind_diag(OSSL_PARAM *params, const char *param_name,
  144. void **pp)
  145. {
  146. const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_name);
  147. if (p != NULL)
  148. *pp = NULL;
  149. }
  150. static int newreno_unbind_diagnostic(OSSL_CC_DATA *cc, OSSL_PARAM *params)
  151. {
  152. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  153. unbind_diag(params, OSSL_CC_OPTION_MAX_DGRAM_PAYLOAD_LEN,
  154. (void **)&nr->p_diag_max_dgram_payload_len);
  155. unbind_diag(params, OSSL_CC_OPTION_CUR_CWND_SIZE,
  156. (void **)&nr->p_diag_cur_cwnd_size);
  157. unbind_diag(params, OSSL_CC_OPTION_MIN_CWND_SIZE,
  158. (void **)&nr->p_diag_min_cwnd_size);
  159. unbind_diag(params, OSSL_CC_OPTION_CUR_BYTES_IN_FLIGHT,
  160. (void **)&nr->p_diag_cur_bytes_in_flight);
  161. unbind_diag(params, OSSL_CC_OPTION_CUR_STATE,
  162. (void **)&nr->p_diag_cur_state);
  163. return 1;
  164. }
  165. static void newreno_update_diag(OSSL_CC_NEWRENO *nr)
  166. {
  167. if (nr->p_diag_max_dgram_payload_len != NULL)
  168. *nr->p_diag_max_dgram_payload_len = nr->max_dgram_size;
  169. if (nr->p_diag_cur_cwnd_size != NULL)
  170. *nr->p_diag_cur_cwnd_size = nr->cong_wnd;
  171. if (nr->p_diag_min_cwnd_size != NULL)
  172. *nr->p_diag_min_cwnd_size = nr->k_min_wnd;
  173. if (nr->p_diag_cur_bytes_in_flight != NULL)
  174. *nr->p_diag_cur_bytes_in_flight = nr->bytes_in_flight;
  175. if (nr->p_diag_cur_state != NULL) {
  176. if (nr->in_congestion_recovery)
  177. *nr->p_diag_cur_state = 'R';
  178. else if (nr->cong_wnd < nr->slow_start_thresh)
  179. *nr->p_diag_cur_state = 'S';
  180. else
  181. *nr->p_diag_cur_state = 'A';
  182. }
  183. }
  184. static int newreno_in_cong_recovery(OSSL_CC_NEWRENO *nr, OSSL_TIME tx_time)
  185. {
  186. return ossl_time_compare(tx_time, nr->cong_recovery_start_time) <= 0;
  187. }
  188. static void newreno_cong(OSSL_CC_NEWRENO *nr, OSSL_TIME tx_time)
  189. {
  190. int err = 0;
  191. /* No reaction if already in a recovery period. */
  192. if (newreno_in_cong_recovery(nr, tx_time))
  193. return;
  194. /* Start a new recovery period. */
  195. nr->in_congestion_recovery = 1;
  196. nr->cong_recovery_start_time = nr->now_cb(nr->now_cb_arg);
  197. /* slow_start_thresh = cong_wnd * loss_reduction_factor */
  198. nr->slow_start_thresh
  199. = safe_muldiv_u64(nr->cong_wnd,
  200. nr->k_loss_reduction_factor_num,
  201. nr->k_loss_reduction_factor_den,
  202. &err);
  203. if (err)
  204. nr->slow_start_thresh = UINT64_MAX;
  205. nr->cong_wnd = nr->slow_start_thresh;
  206. if (nr->cong_wnd < nr->k_min_wnd)
  207. nr->cong_wnd = nr->k_min_wnd;
  208. }
  209. static void newreno_flush(OSSL_CC_NEWRENO *nr, uint32_t flags)
  210. {
  211. if (!nr->processing_loss)
  212. return;
  213. newreno_cong(nr, nr->tx_time_of_last_loss);
  214. if ((flags & OSSL_CC_LOST_FLAG_PERSISTENT_CONGESTION) != 0) {
  215. nr->cong_wnd = nr->k_min_wnd;
  216. nr->cong_recovery_start_time = ossl_time_zero();
  217. }
  218. nr->processing_loss = 0;
  219. newreno_update_diag(nr);
  220. }
  221. static uint64_t newreno_get_tx_allowance(OSSL_CC_DATA *cc)
  222. {
  223. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  224. if (nr->bytes_in_flight >= nr->cong_wnd)
  225. return 0;
  226. return nr->cong_wnd - nr->bytes_in_flight;
  227. }
  228. static OSSL_TIME newreno_get_wakeup_deadline(OSSL_CC_DATA *cc)
  229. {
  230. if (newreno_get_tx_allowance(cc) > 0) {
  231. /* We have TX allowance now so wakeup immediately */
  232. return ossl_time_zero();
  233. } else {
  234. /*
  235. * The NewReno congestion controller does not vary its state in time,
  236. * only in response to stimulus.
  237. */
  238. return ossl_time_infinite();
  239. }
  240. }
  241. static int newreno_on_data_sent(OSSL_CC_DATA *cc, uint64_t num_bytes)
  242. {
  243. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  244. nr->bytes_in_flight += num_bytes;
  245. newreno_update_diag(nr);
  246. return 1;
  247. }
  248. static int newreno_is_cong_limited(OSSL_CC_NEWRENO *nr)
  249. {
  250. uint64_t wnd_rem;
  251. /* We are congestion-limited if we are already at the congestion window. */
  252. if (nr->bytes_in_flight >= nr->cong_wnd)
  253. return 1;
  254. wnd_rem = nr->cong_wnd - nr->bytes_in_flight;
  255. /*
  256. * Consider ourselves congestion-limited if less than three datagrams' worth
  257. * of congestion window remains to be spent, or if we are in slow start and
  258. * have consumed half of our window.
  259. */
  260. return (nr->cong_wnd < nr->slow_start_thresh && wnd_rem <= nr->cong_wnd / 2)
  261. || wnd_rem <= 3 * nr->max_dgram_size;
  262. }
  263. static int newreno_on_data_acked(OSSL_CC_DATA *cc,
  264. const OSSL_CC_ACK_INFO *info)
  265. {
  266. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  267. /*
  268. * Packet has been acked. Firstly, remove it from the aggregate count of
  269. * bytes in flight.
  270. */
  271. nr->bytes_in_flight -= info->tx_size;
  272. /*
  273. * We use acknowledgement of data as a signal that we are not at channel
  274. * capacity and that it may be reasonable to increase the congestion window.
  275. * However, acknowledgement is not a useful signal that there is further
  276. * capacity if we are not actually saturating the congestion window that we
  277. * already have (for example, if the application is not generating much data
  278. * or we are limited by flow control). Therefore, we only expand the
  279. * congestion window if we are consuming a significant fraction of the
  280. * congestion window.
  281. */
  282. if (!newreno_is_cong_limited(nr))
  283. goto out;
  284. /*
  285. * We can handle acknowledgement of a packet in one of three ways
  286. * depending on our current state:
  287. *
  288. * - Congestion Recovery: Do nothing. We don't start increasing
  289. * the congestion window in response to acknowledgements until
  290. * we are no longer in the Congestion Recovery state.
  291. *
  292. * - Slow Start: Increase the congestion window using the slow
  293. * start scale.
  294. *
  295. * - Congestion Avoidance: Increase the congestion window using
  296. * the congestion avoidance scale.
  297. */
  298. if (newreno_in_cong_recovery(nr, info->tx_time)) {
  299. /* Congestion recovery, do nothing. */
  300. } else if (nr->cong_wnd < nr->slow_start_thresh) {
  301. /* When this condition is true we are in the Slow Start state. */
  302. nr->cong_wnd += info->tx_size;
  303. nr->in_congestion_recovery = 0;
  304. } else {
  305. /* Otherwise, we are in the Congestion Avoidance state. */
  306. nr->bytes_acked += info->tx_size;
  307. /*
  308. * Avoid integer division as per RFC 9002 s. B.5. / RFC3465 s. 2.1.
  309. */
  310. if (nr->bytes_acked >= nr->cong_wnd) {
  311. nr->bytes_acked -= nr->cong_wnd;
  312. nr->cong_wnd += nr->max_dgram_size;
  313. }
  314. nr->in_congestion_recovery = 0;
  315. }
  316. out:
  317. newreno_update_diag(nr);
  318. return 1;
  319. }
  320. static int newreno_on_data_lost(OSSL_CC_DATA *cc,
  321. const OSSL_CC_LOSS_INFO *info)
  322. {
  323. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  324. if (info->tx_size > nr->bytes_in_flight)
  325. return 0;
  326. nr->bytes_in_flight -= info->tx_size;
  327. if (!nr->processing_loss) {
  328. if (ossl_time_compare(info->tx_time, nr->tx_time_of_last_loss) <= 0)
  329. /*
  330. * After triggering congestion due to a lost packet at time t, don't
  331. * trigger congestion again due to any subsequently detected lost
  332. * packet at a time s < t, as we've effectively already signalled
  333. * congestion on loss of that and subsequent packets.
  334. */
  335. goto out;
  336. nr->processing_loss = 1;
  337. /*
  338. * Cancel any pending window increase in the Congestion Avoidance state.
  339. */
  340. nr->bytes_acked = 0;
  341. }
  342. nr->tx_time_of_last_loss
  343. = ossl_time_max(nr->tx_time_of_last_loss, info->tx_time);
  344. out:
  345. newreno_update_diag(nr);
  346. return 1;
  347. }
  348. static int newreno_on_data_lost_finished(OSSL_CC_DATA *cc, uint32_t flags)
  349. {
  350. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  351. newreno_flush(nr, flags);
  352. return 1;
  353. }
  354. static int newreno_on_data_invalidated(OSSL_CC_DATA *cc,
  355. uint64_t num_bytes)
  356. {
  357. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  358. nr->bytes_in_flight -= num_bytes;
  359. newreno_update_diag(nr);
  360. return 1;
  361. }
  362. static int newreno_on_ecn(OSSL_CC_DATA *cc,
  363. const OSSL_CC_ECN_INFO *info)
  364. {
  365. OSSL_CC_NEWRENO *nr = (OSSL_CC_NEWRENO *)cc;
  366. nr->processing_loss = 1;
  367. nr->bytes_acked = 0;
  368. nr->tx_time_of_last_loss = info->largest_acked_time;
  369. newreno_flush(nr, 0);
  370. return 1;
  371. }
  372. const OSSL_CC_METHOD ossl_cc_newreno_method = {
  373. newreno_new,
  374. newreno_free,
  375. newreno_reset,
  376. newreno_set_input_params,
  377. newreno_bind_diagnostic,
  378. newreno_unbind_diagnostic,
  379. newreno_get_tx_allowance,
  380. newreno_get_wakeup_deadline,
  381. newreno_on_data_sent,
  382. newreno_on_data_acked,
  383. newreno_on_data_lost,
  384. newreno_on_data_lost_finished,
  385. newreno_on_data_invalidated,
  386. newreno_on_ecn,
  387. };