Skip to content

Commit 24cea66

Browse files
committed
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.
1 parent 6dfc8fb commit 24cea66

File tree

6 files changed

+81
-4
lines changed

6 files changed

+81
-4
lines changed

doc/configuration.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17035,25 +17035,37 @@ proto <name>
1703517035
instance, it is possible to force the http/2 on clear TCP by specifying "proto
1703617036
h2" on the bind line.
1703717037

17038-
quic-cc-algo { cubic | newreno | nocc }[(<args,...>)]
17038+
quic-cc-algo { cubic[-pacing] | newreno | nocc }[(<args,...>)]
1703917039
This is a QUIC specific setting to select the congestion control algorithm
1704017040
for any connection attempts to the configured QUIC listeners. They are similar
1704117041
to those used by TCP.
1704217042

1704317043
Default value: cubic
1704417044

17045+
It is possible to active pacing if the algorithm is compatible. This is done
17046+
by using the suffix "-pacing" after the algorithm name. Pacing purpose is to
17047+
smooth emission of data without burst to reduce network loss. In some
17048+
scenario, it can significantly improve network throughput. However, it can
17049+
also increase CPU usage if haproxy is forced to wait too long between each
17050+
emission. Pacing support is still experimental, as such it requires
17051+
"expose-experimental-directives".
17052+
1704517053
For further customization, a list of parameters can be specified after the
1704617054
algorithm token. It must be written between parenthesis, separated by a comma
1704717055
operator. Each argument is optional and can be empty if needed. Here is the
1704817056
mandatory order of each parameters :
1704917057
- maximum window size in bytes. It must be greater than 10k and smaller than
1705017058
4g. By default "tune.quic.frontend.default-max-window-size" value is used.
17059+
- count of datagrams to emit in a burst if pacing is activated. It must be
17060+
between the default value of 1 and 1024.
1705117061

1705217062
Example:
1705317063
# newreno congestion control algorithm
1705417064
quic-cc-algo newreno
1705517065
# cubic congestion control algorithm with one megabytes as window
1705617066
quic-cc-algo cubic(1m)
17067+
# cubic with pacing on top of it, with burst limited to 12 datagrams
17068+
quic-cc-algo cubic-pacing(,12)
1705717069

1705817070
A special value "nocc" may be used to force a fixed congestion window always
1705917071
set at the maximum size. It is reserved for debugging scenarios to remove any

include/haproxy/listener-t.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ struct bind_conf {
185185
struct quic_cc_algo *quic_cc_algo; /* QUIC control congestion algorithm */
186186
size_t max_cwnd; /* QUIC maximumu congestion control window size (kB) */
187187
enum quic_sock_mode quic_mode; /* QUIC socket allocation strategy */
188+
int quic_pacing_burst; /* QUIC allowed pacing burst size */
188189
#endif
189190
struct proxy *frontend; /* the frontend all these listeners belong to, or NULL */
190191
const struct mux_proto_list *mux_proto; /* the mux to use for all incoming connections (specified by the "proto" keyword) */

include/haproxy/quic_cc.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ static inline void *quic_cc_priv(const struct quic_cc *cc)
8282
* which is true for an IPv4 path, if not false for an IPv6 path.
8383
*/
8484
static inline void quic_cc_path_init(struct quic_cc_path *path, int ipv4, unsigned long max_cwnd,
85-
struct quic_cc_algo *algo, struct quic_conn *qc)
85+
struct quic_cc_algo *algo, int burst,
86+
struct quic_conn *qc)
8687
{
8788
unsigned int max_dgram_sz;
8889

@@ -96,6 +97,7 @@ static inline void quic_cc_path_init(struct quic_cc_path *path, int ipv4, unsign
9697
path->prep_in_flight = 0;
9798
path->in_flight = 0;
9899
path->ifae_pkts = 0;
100+
path->pacing_burst = burst;
99101
quic_cc_init(&path->cc, algo, qc);
100102
}
101103

src/cfgparse-quic.c

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include <haproxy/global.h>
1414
#include <haproxy/listener.h>
1515
#include <haproxy/proxy.h>
16-
#include <haproxy/quic_cc-t.h>
16+
#include <haproxy/quic_cc.h>
1717
#include <haproxy/quic_rules.h>
1818
#include <haproxy/tools.h>
1919

@@ -69,11 +69,38 @@ static unsigned long parse_window_size(const char *kw, char *value,
6969
return 0;
7070
}
7171

72+
/* Parse <value> as a number of datagrams allowed for burst.
73+
*
74+
* Returns the parsed value or 0 on error.
75+
*/
76+
static uint parse_burst(const char *kw, char *value, char **end_opt, char **err)
77+
{
78+
uint burst;
79+
80+
errno = 0;
81+
burst = strtoul(value, end_opt, 0);
82+
if (*end_opt == value || errno != 0) {
83+
memprintf(err, "'%s' : could not parse burst value", kw);
84+
goto fail;
85+
}
86+
87+
if (!burst || burst > 1024) {
88+
memprintf(err, "'%s' : pacing burst value must be between 1 and 1024", kw);
89+
goto fail;
90+
}
91+
92+
return burst;
93+
94+
fail:
95+
return 0;
96+
}
97+
7298
/* parse "quic-cc-algo" bind keyword */
7399
static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
74100
struct bind_conf *conf, char **err)
75101
{
76102
struct quic_cc_algo *cc_algo = NULL;
103+
const char *str_pacing = "-pacing";
77104
const char *algo = NULL;
78105
char *arg;
79106

@@ -100,6 +127,19 @@ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
100127
algo = QUIC_CC_CUBIC_STR;
101128
*cc_algo = quic_cc_algo_cubic;
102129
arg += strlen(QUIC_CC_CUBIC_STR);
130+
131+
if (strncmp(arg, str_pacing, strlen(str_pacing)) == 0) {
132+
if (!experimental_directives_allowed) {
133+
memprintf(err, "'%s' : support for pacing is experimental, must be allowed via a global "
134+
"'expose-experimental-directives'\n", args[cur_arg]);
135+
goto fail;
136+
}
137+
138+
cc_algo->pacing_rate = quic_cc_default_pacing_rate;
139+
cc_algo->pacing_burst = quic_cc_default_pacing_burst;
140+
conf->quic_pacing_burst = 1;
141+
arg += strlen(str_pacing);
142+
}
103143
}
104144
else if (strncmp(arg, QUIC_CC_NO_CC_STR, strlen(QUIC_CC_NO_CC_STR)) == 0) {
105145
/* nocc */
@@ -141,6 +181,26 @@ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
141181
arg = end_opt;
142182
}
143183

184+
if (*++arg == ')')
185+
goto out;
186+
187+
if (*arg != ',') {
188+
uint burst = parse_burst(args[cur_arg], arg, &end_opt, err);
189+
if (!burst)
190+
goto fail;
191+
192+
conf->quic_pacing_burst = burst;
193+
194+
if (*end_opt == ')') {
195+
goto out;
196+
}
197+
else if (*end_opt != ',') {
198+
memprintf(err, "'%s' : cannot parse burst argument for '%s' algorithm", args[cur_arg], algo);
199+
goto fail;
200+
}
201+
arg = end_opt;
202+
}
203+
144204
if (*++arg != ')') {
145205
memprintf(err, "'%s' : too many argument for '%s' algorithm", args[cur_arg], algo);
146206
goto fail;

src/listener.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,7 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file,
20402040
/* Use connection socket for QUIC by default. */
20412041
bind_conf->quic_mode = QUIC_SOCK_MODE_CONN;
20422042
bind_conf->max_cwnd = global.tune.quic_frontend_max_window_size;
2043+
bind_conf->quic_pacing_burst = 0;
20432044
#endif
20442045
LIST_INIT(&bind_conf->listeners);
20452046

src/quic_conn.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
12241224
/* Only one path at this time (multipath not supported) */
12251225
qc->path = &qc->paths[0];
12261226
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
1227-
cc_algo ? cc_algo : default_quic_cc_algo, qc);
1227+
cc_algo ? cc_algo : default_quic_cc_algo,
1228+
l->bind_conf->quic_pacing_burst, qc);
12281229

12291230
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
12301231
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);

0 commit comments

Comments
 (0)