Initialize STARTUP pacing from the actual initial DATA window
This commit is contained in:
1 parent
b349d7b7b2
commit
b39a861a57
3 files changed
+35
-15
No files matched your search
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user