Keep pacing in bytes per second and reprice outstanding shaper debt

This commit is contained in:
evgeny committed 2026-10-23 02:28:11 +02:00
1 parent b39a861a57
commit 7e413db1e1
3 files changed
+93 -22

No files matched your search

+36 -2
View File
@@ -17,6 +17,7 @@ static struct utun_config cfg;
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 unsigned fail_shaper_timer;
static struct bbr_rate_sample last_sample;
static uint32_t last_mss;
@@ -28,6 +29,7 @@ void* __wrap_uasync_call_soon(struct UASYNC* loop, void* arg, timeout_callback_t
void* __real_uasync_set_timeout(struct UASYNC*, int, void*, timeout_callback_t, const char*);
void* __wrap_uasync_set_timeout(struct UASYNC* loop, int delay, void* arg, timeout_callback_t callback, const char* name) {
if (fail_shaper_timer && !strcmp(name, "etcp_shaper")) { fail_shaper_timer--; return NULL; }
if (fail_tx_timer && !strcmp(name, "etcp_tx")) { fail_tx_timer--; return NULL; }
return __real_uasync_set_timeout(loop, delay, arg, callback, name);
}
@@ -73,6 +75,7 @@ static void check_accounting(void) {
static void setup(void) {
memset(&inst, 0, sizeof(inst)); memset(&cfg, 0, sizeof(cfg));
sends = fail_alloc = fail_send = fail_tx_timer = bbr_calls = saw_cwnd_limited = saw_app_limited = 0;
fail_shaper_timer = 0;
ua = uasync_create(); CHECK(ua);
inst.ua = ua; inst.node_id = 1; inst.config = &cfg; cfg.global.bbr_max_cwnd = INFLIGHT_LIM_MAX;
inst.connections = queue_new(ua, 16, 0, 8, "windows");
@@ -296,6 +299,37 @@ static void pacing_reset_close(void) {
teardown();
}
/* Смена ставки пересчитывает долг, а не оставляет таймер от прежней скорости. */
static void pacing_rate_changes(void) {
setup(); b->link_status = 0; conn->rtt_avg_10 = 10000;
etcp_loadbalancer_set_pacing_rate(a, 100);
enqueue(1000); wait_sends(1); enqueue(500);
uint64_t debt = a->shaper_load_time_tb - get_time_tb();
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Pacing below 1Kbit/s: rate=%u debt_tb=%llu", a->bbr_pacing_rate, (unsigned long long)debt);
CHECK(a->shaper_timer && a->bbr_pacing_rate == 100 && debt >= 99000 && debt <= 103000);
etcp_loadbalancer_set_pacing_rate(a, 1000000);
CHECK(a->shaper_load_time_tb <= get_time_tb() + 20);
wait_sends(2); ack_all(); teardown();
setup(); b->link_status = 0; conn->rtt_avg_10 = 10000;
etcp_loadbalancer_set_pacing_rate(a, 1000000);
enqueue(1000); wait_sends(1); enqueue(500);
etcp_loadbalancer_set_pacing_rate(a, 100);
CHECK(a->shaper_timer && a->shaper_load_time_tb > get_time_tb() + 1000);
test_clock_run_for(ua, 200); CHECK(sends == 1 && conn->input_send_q->count == 1);
etcp_loadbalancer_set_pacing_rate(a, 0);
CHECK(!a->shaper_timer && !a->shaper_sub_nanotime);
wait_sends(2); ack_all(); teardown();
}
static void pacing_timer_failure(void) {
setup(); CHECK(etcp_conn_ref_take(conn) == 0); b->link_status = 0;
etcp_loadbalancer_set_pacing_rate(a, 100); fail_shaper_timer = 1;
enqueue(100); uasync_drain_immediate(ua);
CHECK(!fail_shaper_timer && conn->state == 2 && !conn->links);
etcp_conn_ref_free(conn); teardown();
}
/* Отправитель не превращает pending transport в UP до первого INIT/подтверждения сессии. */
static void initial_readiness(void) {
setup(); conn->state = 0; conn->initialized = conn->links_up = conn->tx_state = 0;
@@ -338,7 +372,7 @@ static void immediate_send(void) {
/* Подтверждения входящего потока не должны ждать pacing собственного исходящего DATA. */
static void ack_without_data_pacing(void) {
setup(); b->link_status = 0; a->bandwidth = 1;
setup(); b->link_status = 0; etcp_loadbalancer_set_pacing_rate(a, 125);
a->shaper_timer = uasync_set_timeout(ua, 1000, a, shaper_ready, "test_shaper"); CHECK(a->shaper_timer);
void* timer = a->shaper_timer;
uint64_t load = a->shaper_load_time_tb, fraction = a->shaper_sub_nanotime;
@@ -418,7 +452,7 @@ int main(void) {
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,
retransmission_timeout};
retransmission_timeout, pacing_rate_changes, pacing_timer_failure};
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);