From 599d8ad4215d618d56dc31c6bc59faa3fdf2a64d Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 23 Oct 2026 01:01:25 +0200 Subject: [PATCH] Bound BBR probe windows by the steady-state queue time budget --- src/transport_layer/etcp_bbr.c | 19 +++++++++++++++++++ src/transport_layer/etcp_bbr.h | 6 ++++-- tests/test_etcp_multipath.c | 5 +++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/transport_layer/etcp_bbr.c b/src/transport_layer/etcp_bbr.c index a8981ca8..554bc761 100644 --- a/src/transport_layer/etcp_bbr.c +++ b/src/transport_layer/etcp_bbr.c @@ -797,6 +797,22 @@ static void bbr_bound_cwnd_for_inflight_model(struct bbr* bbr, uint32_t* cwnd, u *cwnd = (uint32_t)(cap < *cwnd ? cap : *cwnd); } +/* Общий предел окна: ACK aggregation и PROBE_UP используют тот же бюджет, а не добавляются после него. */ +static void bbr_bound_cwnd_for_queue_budget(struct bbr* bbr, uint32_t* cwnd, uint32_t mss) { + uint32_t cap = 0, bw = bbr_bw(bbr); + if (bbr->mode == BBR_PROBE_BW && bbr->queue_budget_us && bw && bbr->min_rtt_us != UINT32_MAX) { + uint64_t limit = (uint64_t)bbr_bdp(bbr, bw, BBR_UNIT, mss) + (uint64_t)bw * bbr->queue_budget_us / BW_UNIT; + if (limit < BBR_CWND_MIN_TARGET * mss) limit = BBR_CWND_MIN_TARGET * mss; + cap = limit > UINT32_MAX ? UINT32_MAX : (uint32_t)limit; + } + if (cap != bbr->queue_cwnd_cap) { + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[L%u] queue budget: mode=%u budget_us=%u min_rtt_us=%u DATA_MSS=%u cap=%u previous=%u", + bbr->link_id, bbr->mode, bbr->queue_budget_us, bbr->min_rtt_us, mss, cap, bbr->queue_cwnd_cap); + bbr->queue_cwnd_cap = cap; + } + if (cap && *cwnd > cap) *cwnd = cap; +} + static int bbr_run_fast_path(struct bbr* bbr, int* update_model, const struct bbr_rate_sample* rs, uint32_t sample_bw, uint32_t mss, uint32_t inflight_packets, @@ -828,6 +844,8 @@ void bbr_init(struct bbr* bbr) bbr->initialized = 1; bbr->init_cwnd = BBR_CWND_MIN_TARGET; bbr->prior_cwnd = 0; + bbr->queue_budget_us = BBR_QUEUE_BUDGET_US; + bbr->queue_cwnd_cap = 0; bbr->delivered = 0; bbr->next_rtt_delivered = 0; @@ -927,6 +945,7 @@ void bbr_main(struct bbr* bbr, const struct bbr_rate_sample* rs, bbr_bound_cwnd_for_inflight_model(bbr, &cwnd, mss); out: + bbr_bound_cwnd_for_queue_budget(bbr, &cwnd, mss); bbr_advance_latest_delivery_signals(bbr, rs, sample_bw); bbr->loss_in_cycle |= (rs->lost > 0); if (bbr->on_cwnd_update) bbr->on_cwnd_update(bbr->cwnd_update_ctx, cwnd); diff --git a/src/transport_layer/etcp_bbr.h b/src/transport_layer/etcp_bbr.h index 50818c56..20d66c54 100644 --- a/src/transport_layer/etcp_bbr.h +++ b/src/transport_layer/etcp_bbr.h @@ -5,8 +5,10 @@ * MSS — максимальный DATA-фрагмент normalizer, без заголовков; не wire-MTU и не размер кадра STCP. * Минимальное окно — 4*MSS; PROBE_RTT осушает до max(4*MSS, 0.5*BDP), затем ждёт >=200мс и раунд ACK. * Транспорт применяет окно с верхним ограничением конфига и не повышает его до собственного минимума. - * Целевое окно PROBE_BW включает 2*BDP и ACK-резерв, в PROBE_UP дополнительно 2*MSS; - * фиксированный предел времени физической очереди модель не задаёт. */ + * Целевое окно PROBE_BW включает 2*BDP и ACK-резерв, в PROBE_UP дополнительно 2*MSS. + * Общий предел этого окна — max(4*MSS, BW*(min_rtt+queue_budget_us)); init задаёт бюджет 20мс. + * Это цель по оценке полосы, не гарантия физической задержки: STARTUP/DRAIN, минимум окна, + * неизвестная/изменившаяся полоса и очереди вне наблюдаемого пути могут превышать бюджет. */ #pragma once #include diff --git a/tests/test_etcp_multipath.c b/tests/test_etcp_multipath.c index 247cdf9d..5277aacd 100644 --- a/tests/test_etcp_multipath.c +++ b/tests/test_etcp_multipath.c @@ -71,11 +71,12 @@ void __wrap_bbr_main(struct bbr* model, const struct bbr_rate_sample* sample, ui const struct dummynet_stats* ds = dummynet_get_stats(channels[p], side); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BBR %s mask=%x side=%d path=%u mode=%u cycle=%u sample=%u/%uus rtt=%u " "min_rtt=%u inflight=%u cwnd=%u pace=%u hi=%u lo=%u app=%d limited=%d DATA_MSS=%u " - "bw_q16=%u/%u bw_lo=%u extra=%u/%u queue_max_us=%llu", case_name, enabled_mask, + "bw_q16=%u/%u bw_lo=%u extra=%u/%u budget_us=%u queue_cap=%u queue_max_us=%llu", case_name, enabled_mask, side, p, model->mode, model->cycle_idx, sample->delivered, sample->interval_us, sample->rtt_us, model->min_rtt_us, inflight, *cwnd, *pacing, model->inflight_hi, model->inflight_lo, sample->is_app_limited, limited, mss, model->bw_hi[0], model->bw_hi[1], model->bw_lo, - model->extra_acked[0], model->extra_acked[1], (unsigned long long)ds->max_queue_delay_us); + model->extra_acked[0], model->extra_acked[1], model->queue_budget_us, model->queue_cwnd_cap, + (unsigned long long)ds->max_queue_delay_us); } } }