mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
MEDIUM: quic: define cubic-pacing congestion algorithm
Define a new QUIC congestion algorithm token 'cubic-pacing' for quic-cc-algo bind keyword. This is identical to default cubic implementation, except that pacing is used for STREAM frames emission. This algorithm supports an extra argument to specify a burst size. This is stored into a new bind_conf member named quic_pacing_burst which can be reuse to initialize quic path. Pacing support is still considered experimental. As such, 'cubic-pacing' can only be used with expose-experimental-directives set.
This commit is contained in:
parent
6dfc8fbf1d
commit
24cea66e07
@ -17035,25 +17035,37 @@ proto <name>
|
|||||||
instance, it is possible to force the http/2 on clear TCP by specifying "proto
|
instance, it is possible to force the http/2 on clear TCP by specifying "proto
|
||||||
h2" on the bind line.
|
h2" on the bind line.
|
||||||
|
|
||||||
quic-cc-algo { cubic | newreno | nocc }[(<args,...>)]
|
quic-cc-algo { cubic[-pacing] | newreno | nocc }[(<args,...>)]
|
||||||
This is a QUIC specific setting to select the congestion control algorithm
|
This is a QUIC specific setting to select the congestion control algorithm
|
||||||
for any connection attempts to the configured QUIC listeners. They are similar
|
for any connection attempts to the configured QUIC listeners. They are similar
|
||||||
to those used by TCP.
|
to those used by TCP.
|
||||||
|
|
||||||
Default value: cubic
|
Default value: cubic
|
||||||
|
|
||||||
|
It is possible to active pacing if the algorithm is compatible. This is done
|
||||||
|
by using the suffix "-pacing" after the algorithm name. Pacing purpose is to
|
||||||
|
smooth emission of data without burst to reduce network loss. In some
|
||||||
|
scenario, it can significantly improve network throughput. However, it can
|
||||||
|
also increase CPU usage if haproxy is forced to wait too long between each
|
||||||
|
emission. Pacing support is still experimental, as such it requires
|
||||||
|
"expose-experimental-directives".
|
||||||
|
|
||||||
For further customization, a list of parameters can be specified after the
|
For further customization, a list of parameters can be specified after the
|
||||||
algorithm token. It must be written between parenthesis, separated by a comma
|
algorithm token. It must be written between parenthesis, separated by a comma
|
||||||
operator. Each argument is optional and can be empty if needed. Here is the
|
operator. Each argument is optional and can be empty if needed. Here is the
|
||||||
mandatory order of each parameters :
|
mandatory order of each parameters :
|
||||||
- maximum window size in bytes. It must be greater than 10k and smaller than
|
- maximum window size in bytes. It must be greater than 10k and smaller than
|
||||||
4g. By default "tune.quic.frontend.default-max-window-size" value is used.
|
4g. By default "tune.quic.frontend.default-max-window-size" value is used.
|
||||||
|
- count of datagrams to emit in a burst if pacing is activated. It must be
|
||||||
|
between the default value of 1 and 1024.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
# newreno congestion control algorithm
|
# newreno congestion control algorithm
|
||||||
quic-cc-algo newreno
|
quic-cc-algo newreno
|
||||||
# cubic congestion control algorithm with one megabytes as window
|
# cubic congestion control algorithm with one megabytes as window
|
||||||
quic-cc-algo cubic(1m)
|
quic-cc-algo cubic(1m)
|
||||||
|
# cubic with pacing on top of it, with burst limited to 12 datagrams
|
||||||
|
quic-cc-algo cubic-pacing(,12)
|
||||||
|
|
||||||
A special value "nocc" may be used to force a fixed congestion window always
|
A special value "nocc" may be used to force a fixed congestion window always
|
||||||
set at the maximum size. It is reserved for debugging scenarios to remove any
|
set at the maximum size. It is reserved for debugging scenarios to remove any
|
||||||
|
@ -185,6 +185,7 @@ struct bind_conf {
|
|||||||
struct quic_cc_algo *quic_cc_algo; /* QUIC control congestion algorithm */
|
struct quic_cc_algo *quic_cc_algo; /* QUIC control congestion algorithm */
|
||||||
size_t max_cwnd; /* QUIC maximumu congestion control window size (kB) */
|
size_t max_cwnd; /* QUIC maximumu congestion control window size (kB) */
|
||||||
enum quic_sock_mode quic_mode; /* QUIC socket allocation strategy */
|
enum quic_sock_mode quic_mode; /* QUIC socket allocation strategy */
|
||||||
|
int quic_pacing_burst; /* QUIC allowed pacing burst size */
|
||||||
#endif
|
#endif
|
||||||
struct proxy *frontend; /* the frontend all these listeners belong to, or NULL */
|
struct proxy *frontend; /* the frontend all these listeners belong to, or NULL */
|
||||||
const struct mux_proto_list *mux_proto; /* the mux to use for all incoming connections (specified by the "proto" keyword) */
|
const struct mux_proto_list *mux_proto; /* the mux to use for all incoming connections (specified by the "proto" keyword) */
|
||||||
|
@ -82,7 +82,8 @@ static inline void *quic_cc_priv(const struct quic_cc *cc)
|
|||||||
* which is true for an IPv4 path, if not false for an IPv6 path.
|
* which is true for an IPv4 path, if not false for an IPv6 path.
|
||||||
*/
|
*/
|
||||||
static inline void quic_cc_path_init(struct quic_cc_path *path, int ipv4, unsigned long max_cwnd,
|
static inline void quic_cc_path_init(struct quic_cc_path *path, int ipv4, unsigned long max_cwnd,
|
||||||
struct quic_cc_algo *algo, struct quic_conn *qc)
|
struct quic_cc_algo *algo, int burst,
|
||||||
|
struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
unsigned int max_dgram_sz;
|
unsigned int max_dgram_sz;
|
||||||
|
|
||||||
@ -96,6 +97,7 @@ static inline void quic_cc_path_init(struct quic_cc_path *path, int ipv4, unsign
|
|||||||
path->prep_in_flight = 0;
|
path->prep_in_flight = 0;
|
||||||
path->in_flight = 0;
|
path->in_flight = 0;
|
||||||
path->ifae_pkts = 0;
|
path->ifae_pkts = 0;
|
||||||
|
path->pacing_burst = burst;
|
||||||
quic_cc_init(&path->cc, algo, qc);
|
quic_cc_init(&path->cc, algo, qc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/listener.h>
|
#include <haproxy/listener.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/quic_cc-t.h>
|
#include <haproxy/quic_cc.h>
|
||||||
#include <haproxy/quic_rules.h>
|
#include <haproxy/quic_rules.h>
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
|
|
||||||
@ -69,11 +69,38 @@ static unsigned long parse_window_size(const char *kw, char *value,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse <value> as a number of datagrams allowed for burst.
|
||||||
|
*
|
||||||
|
* Returns the parsed value or 0 on error.
|
||||||
|
*/
|
||||||
|
static uint parse_burst(const char *kw, char *value, char **end_opt, char **err)
|
||||||
|
{
|
||||||
|
uint burst;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
burst = strtoul(value, end_opt, 0);
|
||||||
|
if (*end_opt == value || errno != 0) {
|
||||||
|
memprintf(err, "'%s' : could not parse burst value", kw);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!burst || burst > 1024) {
|
||||||
|
memprintf(err, "'%s' : pacing burst value must be between 1 and 1024", kw);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return burst;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* parse "quic-cc-algo" bind keyword */
|
/* parse "quic-cc-algo" bind keyword */
|
||||||
static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
|
static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
|
||||||
struct bind_conf *conf, char **err)
|
struct bind_conf *conf, char **err)
|
||||||
{
|
{
|
||||||
struct quic_cc_algo *cc_algo = NULL;
|
struct quic_cc_algo *cc_algo = NULL;
|
||||||
|
const char *str_pacing = "-pacing";
|
||||||
const char *algo = NULL;
|
const char *algo = NULL;
|
||||||
char *arg;
|
char *arg;
|
||||||
|
|
||||||
@ -100,6 +127,19 @@ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
|
|||||||
algo = QUIC_CC_CUBIC_STR;
|
algo = QUIC_CC_CUBIC_STR;
|
||||||
*cc_algo = quic_cc_algo_cubic;
|
*cc_algo = quic_cc_algo_cubic;
|
||||||
arg += strlen(QUIC_CC_CUBIC_STR);
|
arg += strlen(QUIC_CC_CUBIC_STR);
|
||||||
|
|
||||||
|
if (strncmp(arg, str_pacing, strlen(str_pacing)) == 0) {
|
||||||
|
if (!experimental_directives_allowed) {
|
||||||
|
memprintf(err, "'%s' : support for pacing is experimental, must be allowed via a global "
|
||||||
|
"'expose-experimental-directives'\n", args[cur_arg]);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_algo->pacing_rate = quic_cc_default_pacing_rate;
|
||||||
|
cc_algo->pacing_burst = quic_cc_default_pacing_burst;
|
||||||
|
conf->quic_pacing_burst = 1;
|
||||||
|
arg += strlen(str_pacing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (strncmp(arg, QUIC_CC_NO_CC_STR, strlen(QUIC_CC_NO_CC_STR)) == 0) {
|
else if (strncmp(arg, QUIC_CC_NO_CC_STR, strlen(QUIC_CC_NO_CC_STR)) == 0) {
|
||||||
/* nocc */
|
/* nocc */
|
||||||
@ -141,6 +181,26 @@ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
|
|||||||
arg = end_opt;
|
arg = end_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*++arg == ')')
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (*arg != ',') {
|
||||||
|
uint burst = parse_burst(args[cur_arg], arg, &end_opt, err);
|
||||||
|
if (!burst)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
conf->quic_pacing_burst = burst;
|
||||||
|
|
||||||
|
if (*end_opt == ')') {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
else if (*end_opt != ',') {
|
||||||
|
memprintf(err, "'%s' : cannot parse burst argument for '%s' algorithm", args[cur_arg], algo);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
arg = end_opt;
|
||||||
|
}
|
||||||
|
|
||||||
if (*++arg != ')') {
|
if (*++arg != ')') {
|
||||||
memprintf(err, "'%s' : too many argument for '%s' algorithm", args[cur_arg], algo);
|
memprintf(err, "'%s' : too many argument for '%s' algorithm", args[cur_arg], algo);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -2040,6 +2040,7 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file,
|
|||||||
/* Use connection socket for QUIC by default. */
|
/* Use connection socket for QUIC by default. */
|
||||||
bind_conf->quic_mode = QUIC_SOCK_MODE_CONN;
|
bind_conf->quic_mode = QUIC_SOCK_MODE_CONN;
|
||||||
bind_conf->max_cwnd = global.tune.quic_frontend_max_window_size;
|
bind_conf->max_cwnd = global.tune.quic_frontend_max_window_size;
|
||||||
|
bind_conf->quic_pacing_burst = 0;
|
||||||
#endif
|
#endif
|
||||||
LIST_INIT(&bind_conf->listeners);
|
LIST_INIT(&bind_conf->listeners);
|
||||||
|
|
||||||
|
@ -1224,7 +1224,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
/* Only one path at this time (multipath not supported) */
|
/* Only one path at this time (multipath not supported) */
|
||||||
qc->path = &qc->paths[0];
|
qc->path = &qc->paths[0];
|
||||||
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
|
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
|
||||||
cc_algo ? cc_algo : default_quic_cc_algo, qc);
|
cc_algo ? cc_algo : default_quic_cc_algo,
|
||||||
|
l->bind_conf->quic_pacing_burst, qc);
|
||||||
|
|
||||||
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
||||||
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
||||||
|
Loading…
Reference in New Issue
Block a user