diff --git a/doc/configuration.txt b/doc/configuration.txt index 0ebfd3291..c61ba1bd6 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1694,6 +1694,7 @@ The following keywords are supported in the "global" section : - tune.quic.reorder-ratio - tune.quic.retry-threshold - tune.quic.socket-owner + - tune.quic.tx-pacing - tune.quic.zero-copy-fwd-send - tune.renice.runtime - tune.renice.startup @@ -4287,6 +4288,19 @@ tune.quic.socket-owner { connection | listener } is used globally, it will be forced on every listener instance, regardless of their individual configuration. +tune.quic.tx-pacing { on | off } + Enables ('on') or disables ('off') pacing support for QUIC emission. By + default it is disabled. The purpose of pacing is to smooth emission of data + to reduce network losses. In most scenario, it can significantly improve + network throughput by avoiding retransmissions. However, it can be useful to + deactivate it for networks with very high bandwidth/low latency + characteristics to prevent unwanted delay and reduce CPU consumption. + + Pacing support is still experimental, as such it requires + "expose-experimental-directives". + + See also the "quic-cc-algo" bind option. + tune.quic.zero-copy-fwd-send { on | off } Enables ('on') of disabled ('off') the zero-copy sends of data for the QUIC multiplexer. It is enabled by default. @@ -17287,40 +17301,31 @@ proto quic-cc-algo { cubic | newreno | bbr | nocc }[()] This is a QUIC specific setting to select the congestion control algorithm - for any connection attempts to the configured QUIC listeners. They are similar - to those used by TCP. + for any connection attempts to the configured QUIC listeners. They are + similar to those used by TCP. + + Pacing can be activated on top of the congestion algorithm to reduce loss and + improve throughput. This is performed via "tune.quic.tx-pacing" experimental + global keyword. Special care is required when using BBR as it relies on + pacing to work as expected. Using BBR without it may cause slowdowns or high + loss rates during transfers. Also note that haproxy's BBR implementation is + also considered as experimental and cannot be enabled without + "expose-experimental-directives". Default value: cubic - It is possible to enable pacing if the algorithm is compatible. This is done - by setting an optional integer argument as described in the next paragraph. - The purpose of pacing is to smooth emission of data to reduce network losses. - In most scenario, it can significantly improve network throughput by avoiding - retransmissions. Pacing support is still experimental, as such it requires - "expose-experimental-directives". Selecting BBR congestion algorithm will - implicitly activate pacing as it relies on it to work as expected. Explicitly - disabling pacing in conjunction with BBR may cause slowdowns or high loss - rates during transfers. Also note that haproxy's BBR implementation is also - considered as experimental and cannot be enabled without - "expose-experimental-directives". - For further customization, a list of parameters can be specified after the algorithm token. It must be written between parenthesis, separated by a comma. Each argument is optional and can be empty if needed. Here is the mandatory order of each parameters : - 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. - - pacing activation. By default, it is set to 0, which means pacing is not - used. To activate it, specify a positive value. Burst size will be - dynamically adjusted to adapt to the network conditions. Example: # newreno congestion control algorithm quic-cc-algo newreno # cubic congestion control algorithm with one megabytes as window quic-cc-algo cubic(1m) - # cubic with pacing activated on top of it - quic-cc-algo cubic(,1) 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 diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h index 6d4a3986c..be4fcc922 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -87,6 +87,7 @@ #define GTUNE_LISTENER_MQ_ANY (GTUNE_LISTENER_MQ_FAIR | GTUNE_LISTENER_MQ_OPT) #define GTUNE_QUIC_CC_HYSTART (1<<29) #define GTUNE_QUIC_NO_UDP_GSO (1<<30) +#define GTUNE_QUIC_NO_PACING (1<<31) #define NO_ZERO_COPY_FWD 0x0001 /* Globally disable zero-copy FF */ #define NO_ZERO_COPY_FWD_PT 0x0002 /* disable zero-copy FF for PT (recv & send are disabled automatically) */ diff --git a/include/haproxy/quic_cc-t.h b/include/haproxy/quic_cc-t.h index 59be6a3e7..5f55eb2ed 100644 --- a/include/haproxy/quic_cc-t.h +++ b/include/haproxy/quic_cc-t.h @@ -137,9 +137,8 @@ struct quic_cc_algo { void (*state_cli)(struct buffer *buf, const struct quic_cc_path *path); void (*hystart_start_round)(struct quic_cc *cc, uint64_t pn); - /* Defined only if pacing is used. */ uint (*pacing_inter)(const struct quic_cc *cc); - uint (*pacing_burst)(const struct quic_cc *cc); + uint (*pacing_burst)(const struct quic_cc *cc); /* only set if pacing integrated with congestion algo */ struct quic_cc_drs *(*get_drs)(struct quic_cc *cc); void (*on_transmit)(struct quic_cc *cc); diff --git a/src/cfgparse-quic.c b/src/cfgparse-quic.c index acb7d0d69..4d3a6f7ea 100644 --- a/src/cfgparse-quic.c +++ b/src/cfgparse-quic.c @@ -70,32 +70,6 @@ static unsigned long parse_window_size(const char *kw, char *value, return 0; } -/* Parse as pacing argument. - * - * Returns the parsed value or a negative error code. - */ -static int parse_pacing(const char *kw, char *value, char **end_opt, char **err) -{ - int pacing; - - errno = 0; - pacing = strtoul(value, end_opt, 0); - if (*end_opt == value || errno != 0) { - memprintf(err, "'%s' : could not parse pacing value", kw); - goto fail; - } - - if (!pacing) { - memprintf(err, "'%s' : pacing value cannot be negative", kw); - goto fail; - } - - return pacing; - - fail: - return -1; -} - /* parse "quic-cc-algo" bind keyword */ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) @@ -183,39 +157,6 @@ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px, arg = end_opt; } - if (*++arg == ')') - goto out; - - if (*arg != ',') { - int pacing = parse_pacing(args[cur_arg], arg, &end_opt, err); - if (pacing < 0) - goto fail; - - if (pacing) { - 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_inter = quic_cc_default_pacing_inter; - } - else if (!(cc_algo->flags & QUIC_CC_ALGO_FL_OPT_PACING)) { - ha_warning("'%s' : '%s' algorithm without pacing may cause slowdowns or high loss rates during transfers\n", - args[cur_arg], algo); - cc_algo->pacing_inter = NULL; - } - - if (*end_opt == ')') { - goto out; - } - else if (*end_opt != ',') { - memprintf(err, "'%s' : cannot parse pacing argument for '%s' algorithm", args[cur_arg], algo); - goto fail; - } - arg = end_opt; - } - if (*++arg != ')') { memprintf(err, "'%s' : too many argument for '%s' algorithm", args[cur_arg], algo); goto fail; @@ -460,6 +401,18 @@ static int cfg_parse_quic_tune_on_off(char **args, int section_type, struct prox else global.tune.options &= ~GTUNE_QUIC_CC_HYSTART; } + else if (strcmp(suffix, "tune.quic.tx-pacing") == 0) { + if (on) { + if (!experimental_directives_allowed) { + memprintf(err, "'%s' : support for pacing is experimental, must be allowed via a global " + "'expose-experimental-directives'\n", args[0]); + return -1; + } + global.tune.options &= ~GTUNE_QUIC_NO_PACING; + } + else + global.tune.options |= GTUNE_QUIC_NO_PACING; + } return 0; } @@ -467,6 +420,7 @@ static int cfg_parse_quic_tune_on_off(char **args, int section_type, struct prox static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "tune.quic.socket-owner", cfg_parse_quic_tune_socket_owner }, { CFG_GLOBAL, "tune.quic.cc-hystart", cfg_parse_quic_tune_on_off }, + { CFG_GLOBAL, "tune.quic.tx-pacing", cfg_parse_quic_tune_on_off }, { CFG_GLOBAL, "tune.quic.cc.cubic.min-losses", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.conn-tx-buffers.limit", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.glitches-threshold", cfg_parse_quic_tune_setting }, diff --git a/src/cfgparse.c b/src/cfgparse.c index e57202a4f..b36be44ea 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -3052,6 +3053,21 @@ int check_config_validity() } /* HTTP && bufsize < 16384 */ #endif +#ifdef USE_QUIC + if (bind_conf->xprt == xprt_get(XPRT_QUIC)) { + const struct quic_cc_algo *cc_algo = bind_conf->quic_cc_algo ? + bind_conf->quic_cc_algo : default_quic_cc_algo; + + if (!(cc_algo->flags & QUIC_CC_ALGO_FL_OPT_PACING) && + global.tune.options & GTUNE_QUIC_NO_PACING) { + ha_warning("Binding [%s:%d] for %s %s: using the selected congestion algorithm without pacing may cause slowdowns or high loss rates during transfers.\n", + bind_conf->file, bind_conf->line, + proxy_type_str(curproxy), curproxy->id); + err_code |= ERR_WARN; + } + } +#endif /* USE_QUIC */ + /* finish the bind setup */ ret = bind_complete_thread_setup(bind_conf, &err_code); if (ret != 0) { diff --git a/src/haproxy.c b/src/haproxy.c index 22a17adc7..561cab067 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -1375,6 +1375,7 @@ static void init_args(int argc, char **argv) #endif #ifdef USE_QUIC global.tune.options |= GTUNE_QUIC_SOCK_PER_CONN; + global.tune.options |= GTUNE_QUIC_NO_PACING; #endif global.tune.options |= GTUNE_STRICT_LIMITS; diff --git a/src/mux_quic.c b/src/mux_quic.c index 474c2983a..d8b43a913 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -40,8 +40,7 @@ static void qmux_ctrl_room(struct qc_stream_desc *, uint64_t room); /* Returns true if pacing should be used for connection. */ static int qcc_is_pacing_active(const struct connection *conn) { - const struct quic_conn *qc = conn->handle.qc; - return !!(qc->path->cc.algo->pacing_inter); + return !(global.tune.options & GTUNE_QUIC_NO_PACING); } static void qcs_free_ncbuf(struct qcs *qcs, struct ncbuf *ncbuf) diff --git a/src/quic_cc_cubic.c b/src/quic_cc_cubic.c index 9783d8f85..8d7b85434 100644 --- a/src/quic_cc_cubic.c +++ b/src/quic_cc_cubic.c @@ -687,6 +687,8 @@ struct quic_cc_algo quic_cc_algo_cubic = { .event = quic_cc_cubic_event, .slow_start = quic_cc_cubic_slow_start, .hystart_start_round = quic_cc_cubic_hystart_start_round, + .pacing_inter = quic_cc_default_pacing_inter, + .pacing_burst = NULL, .state_trace = quic_cc_cubic_state_trace, .state_cli = quic_cc_cubic_state_cli, }; diff --git a/src/quic_cc_newreno.c b/src/quic_cc_newreno.c index 989a7c105..400c5273f 100644 --- a/src/quic_cc_newreno.c +++ b/src/quic_cc_newreno.c @@ -225,6 +225,8 @@ struct quic_cc_algo quic_cc_algo_nr = { .event = quic_cc_nr_event, .slow_start = quic_cc_nr_slow_start, .hystart_start_round = quic_cc_nr_hystart_start_round, + .pacing_inter = quic_cc_default_pacing_inter, + .pacing_burst = NULL, .state_trace = quic_cc_nr_state_trace, }; diff --git a/src/quic_cc_nocc.c b/src/quic_cc_nocc.c index b4088521b..6f0e61c1d 100644 --- a/src/quic_cc_nocc.c +++ b/src/quic_cc_nocc.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -71,6 +72,8 @@ struct quic_cc_algo quic_cc_algo_nocc = { .flags = QUIC_CC_ALGO_FL_OPT_PACING, .init = quic_cc_nocc_init, .event = quic_cc_nocc_event, + .pacing_inter = quic_cc_default_pacing_inter, + .pacing_burst = NULL, .slow_start = quic_cc_nocc_slow_start, .state_trace = quic_cc_nocc_state_trace, };