From d5142706f88f497da7fa256f70c783cd5c4d7623 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Wed, 10 Sep 2025 10:48:12 +0200 Subject: [PATCH] BUG/MINOR: quic: split option for congestion max window size --- doc/configuration.txt | 39 +++++++++++++++---------- include/haproxy/global-t.h | 1 - include/haproxy/quic_conn-t.h | 2 -- include/haproxy/quic_tune-t.h | 4 +++ src/cfgparse-quic.c | 55 +++++++++++++++++++++++++---------- src/haproxy.c | 1 - src/listener.c | 3 +- src/quic_conn.c | 3 +- 8 files changed, 72 insertions(+), 36 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 2dfbf902e..866c47e8b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1891,6 +1891,7 @@ The following keywords are supported in the "global" section : - tune.quic.be.cc.cubic-min-losses - tune.quic.be.cc.hystart - tune.quic.be.cc.max-frame-loss + - tune.quic.be.cc.max-win-size - tune.quic.be.cc.reorder-ratio - tune.quic.be.max-idle-timeout - tune.quic.be.sec.glitches-threshold @@ -1903,6 +1904,7 @@ The following keywords are supported in the "global" section : - tune.quic.fe.cc.cubic-min-losses - tune.quic.fe.cc.hystart - tune.quic.fe.cc.max-frame-loss + - tune.quic.fe.cc.max-win-size - tune.quic.fe.cc.reorder-ratio - tune.quic.fe.max-idle-timeout - tune.quic.fe.sec.glitches-threshold @@ -1915,7 +1917,7 @@ The following keywords are supported in the "global" section : - tune.quic.frontend.max-streams-bidi - tune.quic.frontend.max-tx-mem (deprecated) - tune.quic.frontend.stream-data-ratio - - tune.quic.frontend.default-max-window-size + - tune.quic.frontend.default-max-window-size (deprecated) - tune.quic.listen - tune.quic.max-frame-loss (deprecated) - tune.quic.mem.tx-max @@ -4753,6 +4755,26 @@ tune.quic.max-frame-loss (deprecated) part of the streamlining process apply on QUIC configuration. If used, this setting will only be applied on frontend connections. +tune.quic.be.cc.max-win-size +tune.quic.fe.cc.max-win-size + Sets the default maximum window size for the congestion controller of a + single QUIC connection either on frontend or backend side. The value must be + written as an integer with an optional suffix 'k', 'm' or 'g'. It must be + between 10k and 4g. + + QUIC multiplexer also uses the current congestion window size to determine if + it can allocate new stream buffers on data emission. As such, the maximum + congestion window size also serves as a limit on this allocator. + + The default value is 480k. + + See also the "quic-cc-algo" bind option. + +tune.quic.frontend.default-max-window-size (deprecated) + This keyword has been deprecated in 3.3 and will be removed in 3.5. It is + part of the streamlining process apply on QUIC configuration. If used, this + setting will only be applied on frontend connections. + tune.quic.be.cc.reorder-ratio <0..100, in percent> tune.quic.fe.cc.reorder-ratio <0..100, in percent> The ratio applied to the packet reordering threshold calculated. It may @@ -4934,19 +4956,6 @@ tune.quic.frontend.stream-data-ratio <0..100, in percent> See also: "tune.quic.frontend.max-data-size", "tune.quic.frontend.max-streams-bidi" -tune.quic.frontend.default-max-window-size - Sets the default maximum window size for the congestion controller of a - single QUIC connection. The value must be written as an integer with an - optional suffix 'k', 'm' or 'g'. It must be between 10k and 4g. - - QUIC multiplexer also uses the current congestion window size to determine if - it can allocate new stream buffers on data emission. As such, the maximum - congestion window size also serves as a limit on this allocator. - - The default value is 480k. - - See also the "quic-cc-algo" bind option. - tune.quic.listen { on | off } Disable QUIC transport protocol on the frontend side. All the QUIC listeners will still be created, but they won't listen for incoming datagrams. Hence, @@ -17166,7 +17175,7 @@ quic-cc-algo { cubic | newreno | bbr | nocc }[()] 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. + 4g. By default "tune.quic.fe.cc.max-win-size" value is used. Example: # newreno congestion control algorithm diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h index 2b0bbae21..cc46cd0c1 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -217,7 +217,6 @@ struct global { #ifdef USE_QUIC unsigned int quic_frontend_max_data; unsigned int quic_frontend_max_streams_bidi; - size_t quic_frontend_max_window_size; unsigned int quic_frontend_stream_data_ratio; #endif /* USE_QUIC */ } tune; diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 3dad90434..2123bbf9f 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -91,8 +91,6 @@ typedef unsigned long long ull; #define QUIC_TOKEN_FMT_NEW 0xb7 /* Retry token duration */ #define QUIC_RETRY_DURATION_SEC 10 -/* Default congestion window size. 480 kB, equivalent to the legacy value which was 30*bufsize */ -#define QUIC_DFLT_MAX_WINDOW_SIZE 491520 /* Default ratio applied for max-stream-data-bidi-remote derived from max-data */ #define QUIC_DFLT_FRONT_STREAM_DATA_RATIO 90 diff --git a/include/haproxy/quic_tune-t.h b/include/haproxy/quic_tune-t.h index 42f239f94..e44020e89 100644 --- a/include/haproxy/quic_tune-t.h +++ b/include/haproxy/quic_tune-t.h @@ -9,6 +9,8 @@ /* Default limit of loss detection on a single frame. If exceeded, connection is closed. */ #define QUIC_DFLT_CC_MAX_FRAME_LOSS 10 +/* Default congestion window size. 480 kB, equivalent to the legacy value which was 30*bufsize */ +#define QUIC_DFLT_CC_MAX_WIN_SIZE 491520 /* Default ratio value applied to a dynamic Packet reorder threshold. */ #define QUIC_DFLT_CC_REORDER_RATIO 50 /* in percent */ /* Default max-idle-timeout advertised via TP */ @@ -29,6 +31,7 @@ struct quic_tune { struct { uint cc_cubic_min_losses; uint cc_max_frame_loss; + size_t cc_max_win_size; uint cc_reorder_ratio; uint max_idle_timeout; uint sec_glitches_threshold; @@ -40,6 +43,7 @@ struct quic_tune { struct { uint cc_cubic_min_losses; uint cc_max_frame_loss; + size_t cc_max_win_size; uint cc_reorder_ratio; uint max_idle_timeout; uint sec_glitches_threshold; diff --git a/src/cfgparse-quic.c b/src/cfgparse-quic.c index 757f5ce36..3dad2b17e 100644 --- a/src/cfgparse-quic.c +++ b/src/cfgparse-quic.c @@ -26,6 +26,7 @@ struct quic_tune quic_tune = { .fe = { .cc_max_frame_loss = QUIC_DFLT_CC_MAX_FRAME_LOSS, + .cc_max_win_size = QUIC_DFLT_CC_MAX_WIN_SIZE, .cc_reorder_ratio = QUIC_DFLT_CC_REORDER_RATIO, .max_idle_timeout = QUIC_DFLT_FE_MAX_IDLE_TIMEOUT, .sec_retry_threshold = QUIC_DFLT_SEC_RETRY_THRESHOLD, @@ -34,6 +35,7 @@ struct quic_tune quic_tune = { }, .be = { .cc_max_frame_loss = QUIC_DFLT_CC_MAX_FRAME_LOSS, + .cc_max_win_size = QUIC_DFLT_CC_MAX_WIN_SIZE, .cc_reorder_ratio = QUIC_DFLT_CC_REORDER_RATIO, .max_idle_timeout = QUIC_DFLT_BE_MAX_IDLE_TIMEOUT, .fb_opts = QUIC_TUNE_FB_TX_PACING|QUIC_TUNE_FB_TX_UDP_GSO, @@ -376,6 +378,23 @@ static int cfg_parse_quic_tune_setting(char **args, int section_type, &quic_tune.fe.cc_max_frame_loss; *ptr = arg; } + else if (strcmp(suffix, "be.cc.max-win-size") == 0 || + strcmp(suffix, "fe.cc.max-win-size") == 0) { + size_t *ptr = (suffix[0] == 'b') ? &quic_tune.be.cc_max_win_size : + &quic_tune.fe.cc_max_win_size; + unsigned long cwnd; + char *end_opt; + + cwnd = parse_window_size(args[0], args[1], &end_opt, err); + if (!cwnd) + return -1; + if (*end_opt != '\0') { + memprintf(err, "'%s' : expects an integer value with an optional suffix 'k', 'm' or 'g'", args[0]); + return -1; + } + + *ptr = cwnd; + } else if (strcmp(suffix, "be.cc.reorder-ratio") == 0 || strcmp(suffix, "fe.cc.reorder-ratio") == 0) { uint *ptr = (suffix[0] == 'b') ? &quic_tune.be.cc_reorder_ratio : @@ -402,20 +421,6 @@ static int cfg_parse_quic_tune_setting(char **args, int section_type, } else if (strcmp(suffix, "frontend.max-streams-bidi") == 0) global.tune.quic_frontend_max_streams_bidi = arg; - else if (strcmp(suffix, "frontend.default-max-window-size") == 0) { - unsigned long cwnd; - char *end_opt; - - cwnd = parse_window_size(args[0], args[1], &end_opt, err); - if (!cwnd) - return -1; - if (*end_opt != '\0') { - memprintf(err, "'%s' : expects an integer value with an optional suffix 'k', 'm' or 'g'", args[0]); - return -1; - } - - global.tune.quic_frontend_max_window_size = cwnd; - } else if (strcmp(suffix, "frontend.stream-data-ratio") == 0) { if (arg < 1 || arg > 100) { memprintf(err, "'%s' expects an integer argument between 1 and 100.", args[0]); @@ -431,6 +436,24 @@ static int cfg_parse_quic_tune_setting(char **args, int section_type, quic_tune.fe.cc_cubic_min_losses = arg - 1; ret = 1; } + else if (strcmp(suffix, "frontend.default-max-window-size") == 0) { + unsigned long cwnd; + char *end_opt; + + memprintf(err, "'%s' is deprecated in 3.3 and will be removed in 3.5. " + "Please use the newer keyword syntax 'tune.quic.fe.cc.max-win-size'.", args[0]); + + cwnd = parse_window_size(args[0], args[1], &end_opt, err); + if (!cwnd) + return -1; + if (*end_opt != '\0') { + memprintf(err, "'%s' : expects an integer value with an optional suffix 'k', 'm' or 'g'", args[0]); + return -1; + } + + quic_tune.fe.cc_max_win_size = cwnd; + ret = 1; + } else if (strcmp(suffix, "frontend.glitches-threshold") == 0) { memprintf(err, "'%s' is deprecated in 3.3 and will be removed in 3.5. " "Please use the newer keyword syntax 'tune.quic.fe.sec.glitches-threshold'.", args[0]); @@ -596,13 +619,13 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "tune.quic.mem.tx-max", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.max-data-size", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.max-streams-bidi", cfg_parse_quic_tune_setting }, - { CFG_GLOBAL, "tune.quic.frontend.default-max-window-size", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.stream-data-ratio", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.zero-copy-fwd-send", cfg_parse_quic_tune_on_off }, { CFG_GLOBAL, "tune.quic.fe.cc.cubic-min-losses", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.fe.cc.hystart", cfg_parse_quic_tune_on_off }, { CFG_GLOBAL, "tune.quic.fe.cc.max-frame-loss", cfg_parse_quic_tune_setting }, + { CFG_GLOBAL, "tune.quic.fe.cc.max-win-size", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.fe.cc.reorder-ratio", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.fe.max-idle-timeout", cfg_parse_quic_time }, { CFG_GLOBAL, "tune.quic.fe.sec.glitches-threshold", cfg_parse_quic_tune_setting }, @@ -614,6 +637,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "tune.quic.be.cc.cubic-min-losses", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.be.cc.hystart", cfg_parse_quic_tune_on_off }, { CFG_GLOBAL, "tune.quic.be.cc.max-frame-loss", cfg_parse_quic_tune_setting }, + { CFG_GLOBAL, "tune.quic.be.cc.max-win-size", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.be.cc.reorder-ratio", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.be.max-idle-timeout", cfg_parse_quic_time }, { CFG_GLOBAL, "tune.quic.be.sec.glitches-threshold", cfg_parse_quic_tune_setting }, @@ -625,6 +649,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "tune.quic.cc.cubic.min-losses", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.disable-tx-pacing", cfg_parse_quic_tune_setting0 }, { CFG_GLOBAL, "tune.quic.disable-udp-gso", cfg_parse_quic_tune_setting0 }, + { CFG_GLOBAL, "tune.quic.frontend.default-max-window-size", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.glitches-threshold", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.max-idle-timeout", cfg_parse_quic_time }, { CFG_GLOBAL, "tune.quic.frontend.max-tx-mem", cfg_parse_quic_tune_setting }, diff --git a/src/haproxy.c b/src/haproxy.c index 87a62d9de..d0bf36f0d 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -201,7 +201,6 @@ struct global global = { #ifdef USE_QUIC .quic_frontend_max_data = 0, .quic_frontend_max_streams_bidi = QUIC_TP_DFLT_FRONT_MAX_STREAMS_BIDI, - .quic_frontend_max_window_size = QUIC_DFLT_MAX_WINDOW_SIZE, .quic_frontend_stream_data_ratio = QUIC_DFLT_FRONT_STREAM_DATA_RATIO, #endif /* USE_QUIC */ }, diff --git a/src/listener.c b/src/listener.c index 98deba6df..3bb4cf862 100644 --- a/src/listener.c +++ b/src/listener.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -2081,7 +2082,7 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file, #ifdef USE_QUIC /* Use connection socket for QUIC by default. */ bind_conf->quic_mode = QUIC_SOCK_MODE_CONN; - bind_conf->max_cwnd = global.tune.quic_frontend_max_window_size; + bind_conf->max_cwnd = quic_tune.fe.cc_max_win_size; #endif LIST_INIT(&bind_conf->listeners); diff --git a/src/quic_conn.c b/src/quic_conn.c index 738936892..00a84d606 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -1320,7 +1320,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, qc->max_ack_delay = 0; /* Only one path at this time (multipath not supported) */ qc->path = &qc->paths[0]; - quic_cc_path_init(qc->path, ipv4, l ? l->bind_conf->max_cwnd : 0, + quic_cc_path_init(qc->path, ipv4, + l ? l->bind_conf->max_cwnd : quic_tune.be.cc_max_win_size, cc_algo ? cc_algo : default_quic_cc_algo, qc); if (local_addr)