- 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
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.
quic-cc-algo { cubic | newreno | bbr | nocc }[(<args,...>)]
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
#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) */
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);
return 0;
}
-/* Parse <value> 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)
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;
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;
}
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 },
#include <haproxy/sink.h>
#include <haproxy/mailers.h>
#include <haproxy/namespace.h>
+#include <haproxy/quic_cc-t.h>
#include <haproxy/quic_sock.h>
#include <haproxy/obj_type-t.h>
#include <haproxy/openssl-compat.h>
} /* 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) {
#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;
/* Returns true if pacing should be used for <conn> 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)
.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,
};
.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,
};
*/
#include <haproxy/api-t.h>
+#include <haproxy/quic_cc.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_trace.h>
#include <haproxy/trace.h>
.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,
};