diff --git a/Makefile b/Makefile index df637cf68..26993ee1c 100644 --- a/Makefile +++ b/Makefile @@ -621,7 +621,7 @@ OPTIONS_OBJS += src/quic_conn.o src/mux_quic.o src/h3.o src/xprt_quic.o \ src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o src/quic_ack.o \ src/quic_trace.o src/quic_cli.o src/quic_ssl.o \ src/quic_rx.o src/quic_tx.o src/quic_cid.o src/quic_retry.o\ - src/quic_retransmit.o + src/quic_retransmit.o src/quic_fctl.o endif ifneq ($(USE_QUIC_OPENSSL_COMPAT),) diff --git a/include/haproxy/quic_fctl-t.h b/include/haproxy/quic_fctl-t.h new file mode 100644 index 000000000..933161954 --- /dev/null +++ b/include/haproxy/quic_fctl-t.h @@ -0,0 +1,15 @@ +#ifndef _HAPROXY_QUIC_FCTL_T_H +#define _HAPROXY_QUIC_FCTL_T_H + +#include + +struct quic_fctl { + /* Offset set by peer which must not be exceeded on send. */ + uint64_t limit; + /* Offset which must never exceed limit. */ + uint64_t off_real; + /* Offset which can go beyond limit one time before being blocked. */ + uint64_t off_soft; +}; + +#endif /* _HAPROXY_QUIC_FCTL_T_H */ diff --git a/include/haproxy/quic_fctl.h b/include/haproxy/quic_fctl.h new file mode 100644 index 000000000..8818372ae --- /dev/null +++ b/include/haproxy/quic_fctl.h @@ -0,0 +1,19 @@ +#ifndef _HAPROXY_QUIC_FCTL_H +#define _HAPROXY_QUIC_FCTL_H + +#include + +void qfctl_init(struct quic_fctl *fctl, uint64_t limit); + +int qfctl_rblocked(const struct quic_fctl *fctl); +int qfctl_sblocked(const struct quic_fctl *fctl); + +int qfctl_set_max(struct quic_fctl *fctl, uint64_t val, + int *unblock_soft, int *unblock_real); + +int qfctl_rinc(struct quic_fctl *fctl, uint64_t diff); +int qfctl_sinc(struct quic_fctl *fctl, uint64_t diff); + +uint64_t qfctl_rcap(const struct quic_fctl *fctl); + +#endif /* _HAPROXY_QUIC_FCTL_H */ diff --git a/src/quic_fctl.c b/src/quic_fctl.c new file mode 100644 index 000000000..b797e5517 --- /dev/null +++ b/src/quic_fctl.c @@ -0,0 +1,96 @@ +#include + +#include + +void qfctl_init(struct quic_fctl *fctl, uint64_t limit) +{ + fctl->limit = limit; + fctl->off_real = 0; + fctl->off_soft = 0; +} + +/* Returns true if real limit is blocked for flow control instance. + * This happens if it is equal than current max value. + */ +int qfctl_rblocked(const struct quic_fctl *fctl) +{ + /* Real limit must never be exceeded. */ + BUG_ON(fctl->off_real > fctl->limit); + return fctl->off_real == fctl->limit; +} + +/* Returns true if soft limit is blocked for flow control instance. + * This happens if it is equal or greater than current max value. + */ +int qfctl_sblocked(const struct quic_fctl *fctl) +{ + return fctl->off_soft >= fctl->limit; +} + +/* Set a new maximum value for flow control instance. If current + * offset is already equal or more, the new value is ignored. Additionally, + * and can be used as output parameters to + * detect if the current update result in one or both of these offsets to be + * unblocked. + * + * Returns true if max is incremented else false. + */ +int qfctl_set_max(struct quic_fctl *fctl, uint64_t val, + int *out_unblock_soft, int *out_unblock_real) +{ + int unblock_soft = 0, unblock_real = 0; + int ret = 0; + + if (fctl->limit < val) { + if (fctl->off_soft >= fctl->limit && fctl->off_soft < val) + unblock_soft = 1; + if (fctl->off_real == fctl->limit && fctl->off_real < val) + unblock_real = 1; + + fctl->limit = val; + ret = 1; + } + + if (out_unblock_soft) + *out_unblock_soft = unblock_soft; + if (out_unblock_real) + *out_unblock_real = unblock_real; + + return ret; +} + +/* Increment real offset of flow control instance by . This cannot + * exceed limit. + * + * Returns true if limit is reached after increment. + */ +int qfctl_rinc(struct quic_fctl *fctl, uint64_t diff) +{ + /* Real limit must never be exceeded. */ + BUG_ON(fctl->off_real + diff > fctl->limit); + fctl->off_real += diff; + + return fctl->off_real == fctl->limit; +} + +/* Increment soft offset of flow control instance by . This cannot + * be done if limit was already reached. + * + * Returns true if limit is reached after increment. + */ +int qfctl_sinc(struct quic_fctl *fctl, uint64_t diff) +{ + /* Soft limit must not be incremented if already in excess. */ + BUG_ON(qfctl_sblocked(fctl)); + fctl->off_soft += diff; + + return fctl->off_soft >= fctl->limit; +} + +/* Return the remaining offset before reaching limit. */ +uint64_t qfctl_rcap(const struct quic_fctl *fctl) +{ + /* Real limit must never be exceeded. */ + BUG_ON(fctl->off_real > fctl->limit); + return fctl->limit - fctl->off_real; +}