Initialize STARTUP pacing from the actual initial DATA window

This commit is contained in:
evgeny committed 2026-10-23 02:24:10 +02:00
1 parent b349d7b7b2
commit b39a861a57
3 files changed
+35 -15

No files matched your search

+8 -15
View File
@@ -582,7 +582,6 @@ static void bbr_update_min_rtt(struct bbr* bbr, const struct bbr_rate_sample* rs
(rs->rtt_us < bbr->probe_rtt_min_us || probe_rtt_expired)) {
bbr->probe_rtt_min_us = rs->rtt_us;
bbr->probe_rtt_min_stamp_tb = bbr_now(bbr);
bbr->has_seen_rtt = 1;
}
if (bbr->probe_rtt_min_us == 0)
bbr->probe_rtt_min_us = ~0U;
@@ -715,27 +714,17 @@ static void bbr_update_ack_aggregation(struct bbr* bbr, const struct bbr_rate_sa
bbr->extra_acked[bbr->extra_acked_win_idx] = extra_acked;
}
static void bbr_init_pacing_rate_from_rtt(struct bbr* bbr, uint32_t cwnd, uint32_t* pacing_rate_out)
{
uint64_t bw;
uint32_t rtt_us;
if (bbr->has_seen_rtt && bbr->min_rtt_us != ~0U) {
rtt_us = bbr->min_rtt_us;
if (rtt_us < 1) rtt_us = 1;
} else {
rtt_us = 1000;
}
bw = (uint64_t)cwnd * BW_UNIT / rtt_us;
static void bbr_init_pacing_rate_from_rtt(struct bbr* bbr, uint32_t mss, uint32_t rtt_us, uint32_t* pacing_rate_out) {
uint64_t bw = (uint64_t)bbr->init_cwnd * mss * BW_UNIT / rtt_us;
*pacing_rate_out = bbr_rate_bytes_per_sec(bw, BBR_STARTUP_PACING_GAIN, 1);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[L%u] initial pacing: window=%llu DATA_MSS=%u RTT_us=%u pacing_Bps=%u",
bbr->link_id, (unsigned long long)bbr->init_cwnd * mss, mss, rtt_us, *pacing_rate_out);
}
static void bbr_set_pacing_rate(struct bbr* bbr, uint32_t bw, int gain, uint32_t* pacing_rate_out)
{
uint32_t rate = bbr_bw_to_pacing_rate(bw, gain);
if (!bbr->has_seen_rtt && bbr->min_rtt_us != ~0U)
bbr_init_pacing_rate_from_rtt(bbr, *pacing_rate_out > 0 ? *pacing_rate_out : 4, pacing_rate_out);
if (bbr->full_bw_reached || rate > *pacing_rate_out)
*pacing_rate_out = rate;
}
@@ -913,6 +902,10 @@ void bbr_main(struct bbr* bbr, const struct bbr_rate_sample* rs,
uint32_t sample_bw, bw;
int update_model = 1;
if (!bbr->has_seen_rtt && rs->rtt_us) {
if (bbr->mode == BBR_STARTUP) bbr_init_pacing_rate_from_rtt(bbr, mss, rs->rtt_us, pacing_rate_out);
bbr->has_seen_rtt = 1;
}
bbr->delivered += rs->acked_sacked;
bbr_update_round_start(bbr, rs);
+1
View File
@@ -3,6 +3,7 @@
* Модуль не отправляет пакеты: возвращённые лимиты применяет линк/loadbalancer.
* cwnd/inflight/delivered — байты DATA, interval/rtt — микросекунды, pacing_rate — байты в секунду.
* MSS — максимальный DATA-фрагмент normalizer, без заголовков; не wire-MTU и не размер кадра STCP.
* Первый достоверный RTT в STARTUP инициализирует pacing от начальных 4*MSS, включая короткие служебные DATA.
* Минимальное окно — 4*MSS; PROBE_RTT осушает до max(4*MSS, 0.5*BDP), затем ждёт >=200мс и раунд ACK.
* Транспорт применяет окно с верхним ограничением конфига и не повышает его до собственного минимума.
* Целевое окно PROBE_BW включает 2*BDP и ACK-резерв, в PROBE_UP дополнительно 2*MSS.
+26
View File
@@ -352,6 +352,31 @@ static int test_queue_budget_fast_path(void) {
PASS(); return 0;
}
/* Короткий служебный DATA не должен задавать стартовую скорость всего bulk-потока. */
static int test_initial_pacing(void) {
MSEG("initial pacing uses four DATA MSS and the first valid RTT");
const uint32_t sizes[] = {576, 1400}, rtts[] = {20000, 200000};
for (unsigned i = 0; i < 2; i++) for (unsigned j = 0; j < 2; j++) {
struct bbr s = { .now_tb = 10000 }; bbr_init(&s);
uint32_t cwnd = 4 * sizes[i], pacing = 123;
struct bbr_rate_sample rs = { .acked_sacked = 16, .delivered = 16, .is_app_limited = 1 };
bbr_main(&s, &rs, &cwnd, &pacing, sizes[i], 0, 0);
if (s.has_seen_rtt || pacing != 123) FAIL("unknown RTT must not seed pacing with a guessed duration");
rs.rtt_us = rs.interval_us = rtts[j]; s.now_tb += rtts[j] / 100;
bbr_main(&s, &rs, &cwnd, &pacing, sizes[i], 0, 0);
uint32_t window_rate = (uint64_t)4 * sizes[i] * 1000000 / rtts[j];
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Initial pacing: DATA_MSS=%u RTT_us=%u initial_window_rate=%u pacing=%u",
sizes[i], rtts[j], window_rate, pacing);
if (!s.has_seen_rtt || pacing < 2 * window_rate || pacing > 3 * window_rate)
FAIL("first RTT must seed STARTUP from the initial window, not the 16-byte message");
uint32_t first = pacing;
rs.rtt_us *= 2; s.now_tb += rs.rtt_us / 100;
bbr_main(&s, &rs, &cwnd, &pacing, sizes[i], 0, 0);
if (pacing != first) FAIL("later RTT samples must not restart pacing initialization");
}
PASS(); return 0;
}
int main(void)
{
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
@@ -373,6 +398,7 @@ int main(void)
test_queue_budget();
test_queue_budget_floor_and_startup();
test_queue_budget_fast_path();
test_initial_pacing();
printf("\n=== Results: %d/%d passed, %d failed ===\n", test_passed, test_total, test_failed);
return test_failed ? 1 : 0;
}