Clamp ETCP retransmission timeout to 10 ms through 1 second

This commit is contained in:
evgeny committed 2026-10-23 02:20:10 +02:00
1 parent 4aae1f6151
commit c3ac337ef0
3 files changed
+40 -10

No files matched your search

+9 -5
View File
@@ -31,6 +31,8 @@
#define MAX_INFLIGHT_BYTES 65536 // Initial window
#define RETRANS_K1 32 // RTT multiplier for retrans timeout
#define RETRANS_K2 32 // Jitter multiplier
#define RETRANS_MIN_TB 100 // Минимальный RTO 10мс, в единицах 0.1мс
#define RETRANS_MAX_TB 10000 // Максимальный RTO 1с, в единицах 0.1мс
#define ACK_DELAY_TB 20 // ACK timer delay (2ms in 0.1ms units)
#define BURST_DELAY_FACTOR 4 // Delay before burst
#define BURST_SIZE 5 // Packets in burst (1 delayed + 4 burst)
@@ -1091,12 +1093,14 @@ static void input_queue_cb(struct ll_queue* q, void* arg) {
static void ack_timeout_cb(void* arg);
static void ack_timeout_check(struct ETCP_CONN* etcp) {
uint64_t now = get_time_tb();
int32_t timeout = (etcp->rtt_avg_10 * RETRANS_K1 + etcp->jitter * RETRANS_K2) / 16;
if (timeout<50) timeout=50;
if (timeout>10000) timeout=10000;
int32_t raw_timeout = (etcp->rtt_avg_10 * RETRANS_K1 + etcp->jitter * RETRANS_K2) / 16;
int32_t timeout = raw_timeout;
if (timeout < RETRANS_MIN_TB) timeout = RETRANS_MIN_TB;
if (timeout > RETRANS_MAX_TB) timeout = RETRANS_MAX_TB;
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "starting check, now=%llu, timeout=%llu, rtt_avg_10=%u, jitter=%u",
(unsigned long long)now, (unsigned long long)timeout, etcp->rtt_avg_10, etcp->jitter);
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] RTO: raw_tb=%d timeout_tb=%d bounds_tb=%d..%d RTT_tb=%u jitter_tb=%u now_tb=%llu",
etcp->log_name, raw_timeout, timeout, RETRANS_MIN_TB, RETRANS_MAX_TB,
etcp->rtt_avg_10, etcp->jitter, (unsigned long long)now);
struct ll_entry* current;
+3 -3
View File
@@ -173,8 +173,8 @@ struct ETCP_CONN {
uint16_t rtt_last;// round-trip time
uint16_t tt_last;// transmission time (время пакета до меня)
// uint16_t rxt_last;// recv relative time (бустро регаирует на изменение задержки rx и медленно на изменение задержки tx)
uint16_t rtt_avg_10;
uint16_t jitter;
uint16_t rtt_avg_10; // Максимальный RTT среди links, в 0.1мс; имя историческое
uint16_t jitter; // Среднее по links от link->jitter/32768, в 0.1мс (удвоенный EWMA |ΔRTT|)
uint64_t last_rtt_cb_time; // время последнего вызова ping_update_rtt (0.1ms)
uint32_t bytes_sent_total;
// uint32_t bytes_received_total; // Not used
@@ -190,7 +190,7 @@ struct ETCP_CONN {
uint32_t optimal_inflight; // Sum over links
// Timers
void* retrans_timer; // Retrans check timer
void* retrans_timer; // Общий RTO: 2*(rtt_avg_10+jitter), ограничен 10мс..1с; callback проверяет FIFO
void* ack_resp_timer; // ACK send timer
void* down_timer; // удаление после непрерывного DOWN, включая первоначальное ожидание
uint64_t down_deadline_tb; // сохраняется при наличии NCD handles после истечения таймера
+28 -2
View File
@@ -183,7 +183,7 @@ static void boundaries_ack_burst(void) {
static void late_ack_and_migration(void) {
setup(); fill(a, 8, 1024); enqueue(600);
a->link_status = b->link_status = 0;
test_clock_run_for(ua, 70);
test_clock_run_for(ua, 120);
check_accounting(); CHECK(!conn->input_wait_ack->count && !a->inflight_bytes);
ack(1); ack(1); CHECK(!a->inflight_bytes);
b->link_status = 1; etcp_link_update_inflight_lim(b, 8192);
@@ -255,6 +255,31 @@ static void shaper_ready(void* arg) {
loadbalancer_link_ready(link);
}
/* Реальный RTO: нижняя/верхняя границы и влияние RTT/jitter, без подмены обработчика таймера. */
static void retransmission_timeout(void) {
const struct { uint16_t rtt, jitter; uint32_t expected_tb; } cases[] = {
{0, 0, 100}, {20, 10, 100}, {200, 0, 400}, {200, 75, 550},
{2000, 100, 4200}, {6000, 1000, 10000}, {UINT16_MAX, UINT16_MAX, 10000}
};
for (unsigned i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
setup(); b->link_status = 0;
conn->rtt_avg_10 = cases[i].rtt; conn->jitter = cases[i].jitter;
fill(a, 1, 100);
struct INFLIGHT_PACKET* packet = (struct INFLIGHT_PACKET*)conn->input_wait_ack->head;
uint64_t sent = packet->last_timestamp, deadline = sent + cases[i].expected_tb + 1000;
// Блокируем только новую передачу: статус UP и настоящий retrans-таймер сохраняются.
a->shaper_timer = uasync_set_timeout(ua, cases[i].expected_tb + 10000, a, shaper_ready, "test_rto_shaper");
CHECK(a->shaper_timer);
while (packet->state == INFLIGHT_STATE_WAIT_ACK && get_time_tb() < deadline) test_clock_poll(ua, 10);
uint64_t elapsed = get_time_tb() - sent;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RTO: RTT_tb=%u jitter_tb=%u expected_tb=%u observed_tb=%llu state=%u",
cases[i].rtt, cases[i].jitter, cases[i].expected_tb, (unsigned long long)elapsed, packet->state);
CHECK(packet->state == INFLIGHT_STATE_WAIT_SEND && elapsed >= cases[i].expected_tb && elapsed <= cases[i].expected_tb + 1000);
CHECK(!sends && !a->inflight_bytes && conn->input_send_q->count == 1 && conn->unacked_bytes == 100);
check_accounting(); ack(packet->seq); teardown();
}
}
static void pacing_reset_close(void) {
setup(); b->link_status = 0;
a->shaper_timer = uasync_set_timeout(ua, 20, a, shaper_ready, "test_shaper"); CHECK(a->shaper_timer);
@@ -392,7 +417,8 @@ 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, bbr_probe_window, tcp_initial_window};
config_limits, timer_failure, immediate_send, ack_without_data_pacing, bbr_delivery_clock, bbr_probe_window, tcp_initial_window,
retransmission_timeout};
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);