diff options
Diffstat (limited to 'doc')
-rw-r--r-- | doc/designs/quic-design/congestion-control.md | 293 |
1 files changed, 47 insertions, 246 deletions
diff --git a/doc/designs/quic-design/congestion-control.md b/doc/designs/quic-design/congestion-control.md index d5bb4a1d79..d2c9323695 100644 --- a/doc/designs/quic-design/congestion-control.md +++ b/doc/designs/quic-design/congestion-control.md @@ -1,249 +1,50 @@ Congestion control API design ============================= -This is mostly inspired by the MSQUIC congestion control API -as it was the most isolated one among the libraries. - -The API is designed in a way to be later easily transformed into a -a fetchable implementation API. - -The API is centered around two structures - the `OSSL_CC_METHOD` -structure holding the API calls into the congestion control module -and `OSSL_CC_DATA` opaque type that holds the data of needed -for the operation of the module. - -Most of the information when the functions of the API are -supposed to be called and how the API is designed to operate -should be clear from the comments for the individual -functions in [Appendix A](#appendix-a). - -Changeables ------------ - -As some parameters that the congestion control algorithm needs might -be updated during the lifetime of the connection these parameters -are called changeables. - -The CC implementation will save the pointers to these parameters' -data and will use the current value of the data at the pointer when -it needs to recalculate the allowance and whether the sending is -blocked or not. - -Some examples of changeables - - - `payload_length` - the maximum length of payload that can be sent - in a single UDP packet (not counting UDP and IP headers). - - `smoothed_rtt` - the current round trip time of the connection - as computed from the acked packets. - - `rtt_variance` - the variance of the `smoothed_rtt` value. - -Thread handling ---------------- - -The `OSSL_CC_DATA` is created with the `new` function per each -connection. The expectation is that there is only a single thread -accessing the `ccdata` which should not be a limitation as the calls -into the module will be done from the packetizer layer of the QUIC -connection handling and there the eventual writes from multiple threads -handling individual streams of the connection have to be synchronized -already to create the packets. - -Congestion event handling -------------------------- - -The congestion state of a connection can change only after some event -happens - i.e., a packet is considered lost meaning the `on_data_lost()` -is called, or an ack arrives for a packet causing calls to -`on_data_acked()` or `on_spurious_congestion_event()` functions. -The congestion control does not produce any timer events by itself. - -Exemptions ----------- - -To facilitate probing and to avoid having to always special-case -probing packets when considering congestion on sending, the -`set_exemption()` function allows setting a number of packets that are -allowed to be sent even when forbidden by the eventual congestion state. - -The exemptions must be used if and only if a packet (or multiple packets) -has to be sent as required by the protocol regardless of the congestion state. - -Paths ------ - -Initially the design expects that only a single path per-connection is -actively sending data. In future when multiple active paths sending data -shall be supported the instances of `OSSL_CC_DATA` would be per-path. - -There might need to be further adjustments needed in that case. However -at least initially this API is intended to be internal to the -OpenSSL library allowing any necessary changes of the API. - -Appendix A ----------- - -Proposed header file with comments explaining the individual -functions. The API is meant to be internal initially so the method -accessors to set the individual functions will be added later once -the API is public. Alternatively this might be also implemented -as fetchable dispatch API. - -```C -/* - * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -#include <openssl/ssl.h> - -typedef struct ossl_cc_method_st OSSL_CC_METHOD; -/* - * An OSSL_CC_DATA is an externally defined opaque pointer holding - * the internal data of the congestion control method - */ -typedef struct ossl_cc_data_st OSSL_CC_DATA; - -/* - * Once this becomes public API then we will need functions to create and - * free an OSSL_CC_METHOD, as well as functions to get/set the various - * function pointers....unless we make it fetchable. - */ -struct ossl_cc_method_st { - /* - * Create a new OSSL_CC_DATA object to handle the congestion control - * calculations. - * - * |settings| are mandatory settings that will cause the - * new() call to fail if they are not understood). - * |options| are optional settings that will not - * cause the new() call to fail if they are not understood. - * |changeables| contain additional parameters that the congestion - * control algorithms need that can be updated during the - * connection lifetime - for example size of the datagram payload. - * To avoid calling a function with OSSL_PARAM array every time - * these parameters are changed the addresses of these param - * values are considered permanent and the values can be updated - * any time. - */ - OSSL_CC_DATA *(*new)(OSSL_PARAM *settings, OSSL_PARAM *options, - OSSL_PARAM *changeables); - - /* - * Release the OSSL_CC_DATA. - */ - void (*free)(OSSL_CC_DATA *ccdata); - - /* - * Reset the congestion control state. - * |flags| to support different level of reset (partial/full). - */ - void (*reset)(OSSL_CC_DATA *ccdata, int flags); - - /* - * Set number of packets exempted from CC - used for probing - * |numpackets| is a small value (2). - * Returns 0 on error, 1 otherwise. - */ - int (*set_exemption)(OSSL_CC_DATA *ccdata, int numpackets); - - /* - * Get current number of packets exempted from CC. - * Returns negative value on error, the number otherwise. - */ - int (*get_exemption)(OSSL_CC_DATA *ccdata); - - /* - * Returns 1 if sending is allowed, 0 otherwise. - */ - int (*can_send)(OSSL_CC_DATA *ccdata); - - /* - * Returns number of bytes allowed to be sent. - * |time_since_last_send| is time since last send operation - * in microseconds. - * |time_valid| is 1 if the |time_since_last_send| holds - * a meaningful value, 0 otherwise. - */ - size_t (*get_send_allowance)(OSSL_CC_DATA *ccdata, - uint64_t time_since_last_send, - int time_valid); - - /* - * Returns the maximum number of bytes allowed to be in flight. - */ - size_t (*get_bytes_in_flight_max)(OSSL_CC_DATA *ccdata); - - /* - * Returns the next time at which the CC will release more budget for - * sending, or ossl_time_infinite(). - */ - OSSL_TIME (*get_next_credit_time)(OSSL_CC_DATA *ccdata); - - /* - * To be called when a packet with retransmittable data was sent. - * |num_retransmittable_bytes| is the number of bytes sent - * in the packet that are retransmittable. - * Returns 1 on success, 0 otherwise. - */ - int (*on_data_sent)(OSSL_CC_DATA *ccdata, - size_t num_retransmittable_bytes); - - /* - * To be called when retransmittable data was invalidated. - * I.E. they are not considered in-flight anymore but - * are neither acknowledged nor lost. In particular used when - * 0RTT data was rejected. - * |num_retransmittable_bytes| is the number of bytes - * of the invalidated data. - * Returns 1 if sending is unblocked (can_send returns 1), 0 - * otherwise. - */ - int (*on_data_invalidated)(OSSL_CC_DATA *ccdata, - size_t num_retransmittable_bytes); - - /* - * To be called when sent data was acked. - * |time_now| is current time in microseconds. - * |largest_pn_acked| is the largest packet number of the acked - * packets. - * |num_retransmittable_bytes| is the number of retransmittable - * packet bytes that were newly acked. - * Returns 1 if sending is unblocked (can_send returns 1), 0 - * otherwise. - */ - int (*on_data_acked)(OSSL_CC_DATA *ccdata, - uint64_t time_now, - uint64_t last_pn_acked, - size_t num_retransmittable_bytes); - - /* - * To be called when sent data is considered lost. - * |largest_pn_lost| is the largest packet number of the lost - * packets. - * |largest_pn_sent| is the largest packet number sent on this - * connection. - * |num_retransmittable_bytes| is the number of retransmittable - * packet bytes that are newly considered lost. - * |persistent_congestion| is 1 if the congestion is considered - * persistent (see RFC 9002 Section 7.6), 0 otherwise. - */ - void (*on_data_lost)(OSSL_CC_DATA *ccdata, - uint64_t largest_pn_lost, - uint64_t largest_pn_sent, - size_t num_retransmittable_bytes, - int persistent_congestion); - - /* - * To be called when all lost data from the previous call to - * on_data_lost() was actually acknowledged. - * This reverts the size of the congestion window to the state - * before the on_data_lost() call. - * Returns 1 if sending is unblocked, 0 otherwise. - */ - int (*on_spurious_congestion_event)(OSSL_CC_DATA *ccdata); -}; -``` +We use an abstract interface for the QUIC congestion controller to facilitate +use of pluggable QUIC congestion controllers in the future. The interface is +based on interfaces suggested by RFC 9002 and MSQUIC's congestion control APIs. + +`OSSL_CC_METHOD` provides a vtable of function pointers to congestion controller +methods. `OSSL_CC_DATA` is an opaque type representing a congestion controller +instance. + +For details on the API, see the comments in `include/internal/quic_cc.h`. + +Congestion controllers are not thread safe; the caller is responsible for +synchronisation. + +Congestion controllers may vary their state with respect to time. This is +faciliated via the `get_wakeup_deadline` method and the `now` argument to the +`new` method, which provides access to a clock. While no current congestion +controller makes use of this facility, it can be used by future congestion +controllers to implement packet pacing. + +Congestion controllers may expose integer configuration options via the +`set_option_uint` and `get_option_uint` methods. These options may be specific +to the congestion controller method, although there are some well known ones +intended to be common to all congestion controllers. The use of strings for +option names is avoided for performance reasons. + +Currently, the only dependency injected to a congestion controller is access to +a clock. In the future it is likely that access at least to the statistics +manager will be provided. Excessive futureproofing of the congestion controller +interface has been avoided as this is currently an internal API for which no API +stability guarantees are required; for example, no currently implemented +congestion control algorithm requires access to the statistics manager, but such +access can readily be added later as needed. + +QUIC congestion control state is per-path, per-connection. Currently we support +only a single path per connection, so there is one congestion control instance +per connection. This may change in future. + +While the congestion control API is roughly based around the arrangement of +functions as described by the congestion control psuedocode in RFC 9002, there +are some deliberate changes in order to obtain cleaner separation between the +loss detection and congestion control functions. Where a literal option of RFC +9002 psuedocode would require a congestion controller to access the ACK +manager's internal state directly, the interface between the two has been +changed to avoid this. This involves some small amounts of functionality which +RFC 9002 considers part of the congestion controller being part of the ACK +manager in our implementation. See the comments in `include/internal/quic_cc.h` +and `ssl/quic/quic_ackm.c` for more information. |