Cover BBR probe drain and transport DATA window consistency

This commit is contained in:
evgeny committed 2026-10-23 00:42:02 +02:00
1 parent 519d6e4eaf
commit 92a535e009
2 files changed
+69 -1

No files matched your search

+28
View File
@@ -253,6 +253,33 @@ static int test_probe_rtt(void)
PASS(); return 0;
}
/* Длинный канал осушается до половины BDP, затем выдерживает 200мс и полный раунд доставки. */
static int test_probe_rtt_lifecycle(void) {
MSEG("PROBE_RTT half-BDP drain, hold and window restore");
struct bbr s = { .now_tb = 10000 }; bbr_init(&s);
s.mode = BBR_PROBE_BW; s.full_bw_reached = 1; s.cycle_idx = BBR_BW_PROBE_CRUISE;
s.bw_hi[0] = BW_UNIT; s.min_rtt_us = s.probe_rtt_min_us = 200000; s.has_seen_rtt = 1;
s.now_tb += BBR_PROBE_RTT_WIN_MS * 10 + 1;
uint32_t cwnd = 400000, pacing = 1000000;
struct bbr_rate_sample rs = { .delivered = 20000, .interval_us = 20000, .rtt_us = 200000,
.acked_sacked = 1000, .tx_in_flight = 400000 };
bbr_main(&s, &rs, &cwnd, &pacing, 1400, 200001, 0);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PROBE_RTT drain: BDP=200000 cwnd=%u timer=%llu", cwnd,
(unsigned long long)s.probe_rtt_done_stamp_tb);
if (s.mode != BBR_PROBE_RTT || cwnd != 100000) FAIL("probe window must be half of 200000-byte BDP");
if (s.probe_rtt_done_stamp_tb) FAIL("probe hold started before flight drained");
s.now_tb++; rs.prior_delivered = s.delivered; rs.is_app_limited = 1;
bbr_main(&s, &rs, &cwnd, &pacing, 1400, 100000, 0);
if (!s.probe_rtt_done_stamp_tb) FAIL("probe hold did not start at half-BDP");
s.now_tb += BBR_PROBE_RTT_MS * 10 - 1; rs.prior_delivered = s.delivered;
bbr_main(&s, &rs, &cwnd, &pacing, 1400, 100000, 0);
if (s.mode != BBR_PROBE_RTT || cwnd != 100000) FAIL("probe ended before 200ms");
s.now_tb++; rs.prior_delivered = s.delivered;
bbr_main(&s, &rs, &cwnd, &pacing, 1400, 100000, 0);
if (s.mode != BBR_PROBE_BW || cwnd < 400000) FAIL("probe did not restore the previous window after hold and round");
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);
@@ -270,6 +297,7 @@ int main(void)
test_loss_cut();
test_fast_path();
test_probe_rtt();
test_probe_rtt_lifecycle();
printf("\n=== Results: %d/%d passed, %d failed ===\n", test_passed, test_total, test_failed);
return test_failed ? 1 : 0;
}
+41 -1
View File
@@ -18,6 +18,7 @@ static struct ETCP_CONN* conn;
static struct ETCP_LINK *a, *b;
static unsigned sends, fail_alloc, fail_send, fail_tx_timer, bbr_calls, saw_cwnd_limited, saw_app_limited;
static struct bbr_rate_sample last_sample;
static uint32_t last_mss;
void* __real_uasync_call_soon(struct UASYNC*, void*, timeout_callback_t);
void* __wrap_uasync_call_soon(struct UASYNC* loop, void* arg, timeout_callback_t callback) {
@@ -49,6 +50,7 @@ void __wrap_bbr_main(struct bbr* model, const struct bbr_rate_sample* sample, ui
uint32_t* pacing, uint32_t mss, uint32_t inflight, int limited) {
bbr_calls++; saw_cwnd_limited |= !!limited; saw_app_limited |= !!sample->is_app_limited;
last_sample = *sample;
last_mss = mss;
__real_bbr_main(model, sample, cwnd, pacing, mss, inflight, limited);
}
@@ -344,6 +346,44 @@ static void bbr_delivery_clock(void) {
teardown();
}
/* Модель и транспорт используют один DATA MSS и одно окно, в том числе ниже 8KiB. */
static void bbr_probe_window(void) {
setup(); b->link_status = 0; conn->rtt_avg_10 = 10000;
etcp_update_mtu(conn);
uint32_t minimum = BBR_CWND_MIN_TARGET * conn->normalizer->frag_size;
a->rtt_last = 100;
a->bbr = u_calloc(1, sizeof(*a->bbr)); CHECK(a->bbr);
a->bbr->now_tb = 10000; bbr_init(a->bbr);
a->bbr->mode = BBR_PROBE_BW; a->bbr->full_bw_reached = 1; a->bbr->cycle_idx = BBR_BW_PROBE_CRUISE;
a->bbr->min_rtt_us = a->bbr->probe_rtt_min_us = 10000; a->bbr->has_seen_rtt = 1;
a->bbr->bw_hi[0] = BW_UNIT / 10; a->bbr->now_tb += BBR_PROBE_RTT_WIN_MS * 10 + 1;
fill(a, 8, 1000); test_clock_run_for(ua, 200); ack(1);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PROBE_RTT transport: DATA_MSS=%u model_MSS=%u window=%u expected=%u",
conn->normalizer->frag_size, last_mss, a->inflight_lim_bytes, minimum);
CHECK(last_mss == conn->normalizer->frag_size && a->inflight_lim_bytes == minimum && minimum < INFLIGHT_LIM_MIN);
struct INFLIGHT_PACKET* probe = enqueue(1000); CHECK(!request());
ack(2); ack(3); ack(4); CHECK(a->bbr->probe_rtt_done_stamp_tb);
struct ETCP_DGRAM* pkt = request(); CHECK(pkt && probe->is_app_limited);
memory_pool_free(inst.pkt_pool, pkt);
test_clock_run_for(ua, 10); a->bbr->now_tb += BBR_PROBE_RTT_MS * 10;
ack(probe->seq); CHECK(a->bbr->mode == BBR_PROBE_BW);
uint32_t window = a->inflight_lim_bytes;
etcp_link_update_inflight_lim(a, 0); CHECK(a->inflight_lim_bytes == window);
etcp_link_update_inflight_lim(a, UINT32_MAX); CHECK(a->inflight_lim_bytes == conn->max_inflight);
ack_all(); teardown();
}
/* Размер STCP-кадра 65535 не является размером DATA-фрагмента и не задаёт начальное окно. */
static void tcp_initial_window(void) {
setup();
struct ETCP_LINK* link = etcp_link_new(conn, NULL, NULL, 1); CHECK(link && link->is_tcp);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "STCP initial window: frame=%u DATA_MSS=%u window=%u", link->mtu,
conn->normalizer->frag_size, link->inflight_lim_bytes);
CHECK(link->inflight_lim_bytes == BBR_CWND_MIN_TARGET * conn->normalizer->frag_size);
CHECK(link->bbr->init_cwnd == BBR_CWND_MIN_TARGET);
teardown();
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_DEBUG);
@@ -351,7 +391,7 @@ int main(void) {
size_t baseline = u_get_allocated_count();
void (*cases[])(void) = {selection, boundaries_ack_burst, late_ack_and_migration, lost_ack_and_shrink,
grow_and_session, allocation_and_send_failure, removed_link_and_bbr, pacing_reset_close, initial_readiness,
config_limits, timer_failure, immediate_send, ack_without_data_pacing, bbr_delivery_clock};
config_limits, timer_failure, immediate_send, ack_without_data_pacing, bbr_delivery_clock, bbr_probe_window, tcp_initial_window};
for (unsigned i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
cases[i](); CHECK(u_get_allocated_count() == baseline);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS ETCP window scenario %u", i);