diff --git a/include/haproxy/quic_pacing-t.h b/include/haproxy/quic_pacing-t.h index 2c7d9338f..f910c78e0 100644 --- a/include/haproxy/quic_pacing-t.h +++ b/include/haproxy/quic_pacing-t.h @@ -6,7 +6,8 @@ struct quic_pacer { const struct quic_cc *cc; /* Congestion controler algo used for this connection */ - ullong next; /* Nanosecond timestamp at which the next emission should be conducted */ + ullong cur; /* Nanosecond timestamp of the last credit reloading */ + uint credit; /* Number of packets which can be emitted in a single burst */ int last_sent; /* Number of datagrams sent during last paced emission */ }; diff --git a/include/haproxy/quic_pacing.h b/include/haproxy/quic_pacing.h index 955f167dd..17e6469a2 100644 --- a/include/haproxy/quic_pacing.h +++ b/include/haproxy/quic_pacing.h @@ -10,11 +10,14 @@ static inline void quic_pacing_init(struct quic_pacer *pacer, const struct quic_cc *cc) { pacer->cc = cc; - pacer->next = 0; + pacer->cur = 0; + pacer->credit = cc->algo->pacing_burst(cc); } int quic_pacing_expired(const struct quic_pacer *pacer); void quic_pacing_sent_done(struct quic_pacer *pacer, int sent); +int quic_pacing_reload(struct quic_pacer *pacer); + #endif /* _HAPROXY_QUIC_PACING_H */ diff --git a/src/mux_quic.c b/src/mux_quic.c index f0766343a..b324e4e7a 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -2533,8 +2533,8 @@ static int qcc_io_send(struct qcc *qcc, int after_pacing) goto out; } - if (qcc_is_pacing_active(qcc->conn)) { - if (!LIST_ISEMPTY(frms) && !quic_pacing_expired(&qcc->tx.pacer)) { + if (!LIST_ISEMPTY(frms) && qcc_is_pacing_active(qcc->conn)) { + if (!quic_pacing_reload(&qcc->tx.pacer)) { if (!after_pacing) ++qcc->tx.paced_sent_ctr; tasklet_wakeup(qcc->wait_event.tasklet, TASK_F_UEVT1); diff --git a/src/quic_cc.c b/src/quic_cc.c index 603914cd4..2365960c1 100644 --- a/src/quic_cc.c +++ b/src/quic_cc.c @@ -54,7 +54,7 @@ void quic_cc_state_trace(struct buffer *buf, const struct quic_cc *cc) uint quic_cc_default_pacing_inter(const struct quic_cc *cc) { struct quic_cc_path *path = container_of(cc, struct quic_cc_path, cc); - return path->loss.srtt * 1000000 / (path->cwnd / path->mtu + 1); + return path->loss.srtt * 1000000 / (path->cwnd / path->mtu + 1) + 1; } /* Return the max number of datagrams which can be emitted in a burst with diff --git a/src/quic_pacing.c b/src/quic_pacing.c index 19216858c..0c4c0ad83 100644 --- a/src/quic_pacing.c +++ b/src/quic_pacing.c @@ -6,12 +6,40 @@ /* Returns true if timer is expired and emission can be retried. */ int quic_pacing_expired(const struct quic_pacer *pacer) { - return !pacer->next || pacer->next <= task_mono_time(); + return pacer->credit; } /* Notify about an emission of count of datagrams. */ void quic_pacing_sent_done(struct quic_pacer *pacer, int sent) { - pacer->next = task_mono_time() + pacer->cc->algo->pacing_inter(pacer->cc) * sent; + BUG_ON(!pacer->credit || pacer->credit < sent); + pacer->credit -= sent; + pacer->last_sent = sent; } + +/* Reload credit when a new emission sequence is initiated. A maximal + * value is calculated if previous emission occurred long time enough. + * + * Returns the remaining credit or 0 if emission cannot be conducted this time. + */ +int quic_pacing_reload(struct quic_pacer *pacer) +{ + const uint64_t task_now_ns = task_mono_time(); + const uint64_t inter = pacer->cc->algo->pacing_inter(pacer->cc); + uint64_t inc; + uint credit_max; + + if (task_now_ns > pacer->cur) { + /* Calculate number of packets which could have been emitted since last emission sequence. Result is rounded up. */ + inc = (task_now_ns - pacer->cur + inter - 1) / inter; + + credit_max = pacer->cc->algo->pacing_burst(pacer->cc); + pacer->credit = MIN(pacer->credit + inc, credit_max); + + /* Refresh pacing reload timer. */ + pacer->cur = task_now_ns; + } + + return pacer->credit; +} diff --git a/src/quic_tx.c b/src/quic_tx.c index 19bcad018..d283ec9be 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -503,7 +503,7 @@ enum quic_tx_err qc_send_mux(struct quic_conn *qc, struct list *frms, } if (pacer) { - max_dgram = qc->path->cc.algo->pacing_burst(&qc->path->cc); + max_dgram = pacer->credit; BUG_ON(max_dgram <= 0); /* pacer must specify a positive burst value. */ }