Exercise RTT changes through real ETCP and document RTO inputs

This commit is contained in:
evgeny committed 2026-10-24 17:26:59 +02:00
1 parent ef404da44c
commit 68a225baec
3 files changed
+50 -2

No files matched your search

+5 -2
View File
@@ -51,10 +51,13 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
после сканирования проверяем появились ли в linked-list собранные данные которые можно переместить в выходную очередь. и перемещаем если есть.
Таймер попыток каждого линка (etcp_tx.c):
- RTO = clamp(ceil(1.2*data_rtt_us + 1.5*data_jitter_us + 3000), 10000, 1000000) мкс.
- RTO = clamp(ceil(1.2*RTT_for_RTO + 1.5*data_jitter_us + 3000), 10000, 1000000) мкс,
RTT_for_RTO=max(data_rtt_us,100*rtt_last): DATA EWMA и последний timestamp/keepalive RTT своего линка.
Целочисленный расчёт использует uint64 и округляет вверх до целой микросекунды.
RTT использует собственный DATA ACK минус receiver_delay; EWMA RTT=1/8, jitter=1/4 |RTT-EWMA|.
Первый RTT задаёт jitter=RTT/2. До DATA ACK используется RTT keepalive этого же пути, без него RTO=1с.
Первый DATA RTT задаёт jitter=RTT/2. Timestamp-RTT задаёт нижнюю границу срока, чтобы после роста задержки
попытки доживали до ACK и DATA-измерения продолжались; BBR RTT/min_rtt по этому сигналу не обновляются.
Без обоих измерений RTO=1с.
- FIFO содержит физические попытки в порядке принятия транспортом. Таймер назначается по старейшей;
при изменении RTO проверяется новый срок. Допускается оставить уже запланированную более раннюю проверку.
uasync округляет задержку вверх до миллисекунды; callback может выполниться позже расчётного срока.
+1
View File
@@ -521,6 +521,7 @@ static int test_startup_delay_exit(void) {
int main(void)
{
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_DEBUG);
test_byte_rate();
test_large_rate();
test_probe_growth_bytes();
+44
View File
@@ -818,6 +818,46 @@ static void stalled(const struct profile* settings, unsigned count, unsigned bro
teardown();
}
/* Смена базового RTT при небольшой нагрузке: DATA-модель оживает, min_rtt стареет и принимает снижение без ожидания. */
static void changing_rtt(void) {
struct profile setting = {20,8000};
low_load = 1; setup(&setting,1,1);
run_for(3000,1,"rtt-low"); begin_measure();
struct ETCP_LINK* link = ep[0].conn->links;
CHECK(link && !link->next && link->bbr);
expect(link->bbr->min_rtt_us < 50000,"initial RTT minimum below 50ms",link->bbr->min_rtt_us,50000);
const unsigned rtts[] = {100,20};
const char* phases[] = {"rtt-high","rtt-low-again"};
for (unsigned n = 0; n < 2; n++) {
uint64_t old_acks = link->acked_packets;
for (int dir = 0; dir < 2; dir++) CHECK(dummynet_set_direction(channels[0],dir,rtts[n]/2,0,8000,512,0,
"127.0.0.1",ss_get_port(&ep[1-dir].sockets[0]->local_addr)) == 0);
profiles[0].rtt_ms = rtts[n];
run_for(1000,1,phases[n]);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RTT change target_ms=%u DATA_us=%u KA_us=%u jitter_us=%u RTO_us=%u min_us=%u "
"ACKs=%llu->%llu stalled=%u retries=%u",
rtts[n],link->data_rtt_us,link->rtt_last*100,link->data_jitter_us,etcp_tx_rto_us(link),link->bbr->min_rtt_us,
(unsigned long long)old_acks,(unsigned long long)link->acked_packets,link->tx_stalled,ep[0].conn->retransmissions_count);
expect(link->acked_packets > old_acks+5,"DATA ACK model progresses after RTT change",link->acked_packets,old_acks+6);
expect(!link->tx_stalled,"live path recovered after RTT change",link->tx_stalled,0);
if (!n) {
expect(link->data_rtt_us > 50000,"DATA RTT learns the longer path",link->data_rtt_us,50001);
run_for(11000,1,"rtt-high-aged");
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RTT high aged: DATA_us=%u min_us=%u stamp_age_tb=%llu probe_us=%u ACKs=%llu",
link->data_rtt_us,link->bbr->min_rtt_us,(unsigned long long)(get_time_tb()-link->bbr->min_rtt_stamp_tb),
link->bbr->probe_rtt_min_us,(unsigned long long)link->acked_packets);
expect(link->bbr->min_rtt_us >= 100000,"old short RTT minimum expires on the longer path",link->bbr->min_rtt_us,100000);
expect(link->bbr->min_rtt_us < 130000,"new RTT minimum stays close to the 100ms path",link->bbr->min_rtt_us,130000);
} else {
expect(link->bbr->min_rtt_us < 50000,"lower RTT minimum learned within 1s",link->bbr->min_rtt_us,50000);
}
}
measuring = 0; run_for(10000,0,"drain");
expect(ep[0].sent == ep[1].received,"complete ordered delivery after RTT changes",ep[1].received,ep[0].sent);
expect(!ep[0].conn->unacked_bytes,"no unacked DATA after RTT change drain",ep[0].conn->unacked_bytes,0);
teardown();
}
int main(int argc, char** argv) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); test_clock_init(10);
log_bbr_samples = getenv("UTUN_BBR_SAMPLES") != NULL;
@@ -898,6 +938,10 @@ int main(int argc, char** argv) {
stalled(settings, faults[n].count, faults[n].broken, faults[n].directions, faults[n].mode, faults[n].sparse, faults[n].ms);
CHECK(u_get_allocated_count() == baseline);
}
if (argc == 1 || !strcmp(argv[1],"rtt-change")) {
selected++; case_name = "rtt-change"; bidirectional = 0;
changing_rtt(); CHECK(u_get_allocated_count() == baseline);
}
CHECK(selected && argc <= 2);
if (csv) CHECK(fclose(csv) == 0);
if (diagnostic_file) {