Keep pacing in bytes per second and reprice outstanding shaper debt
This commit is contained in:
1 parent
b39a861a57
commit
7e413db1e1
3 files changed
+93
-22
No files matched your search
@@ -6,16 +6,57 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h> // For UINT64_MAX
|
||||
#include <time.h> // For timespec
|
||||
#include <math.h> // For rounding
|
||||
#include <limits.h>
|
||||
#include "../lib/mem.h"
|
||||
|
||||
// Enable comprehensive debug output for loadbalancer module
|
||||
#define DEBUG_CATEGORY_LOADBALANCER 1
|
||||
#define ALLOWED_DELTA 5 // 0.5ms allowed gap for time correction
|
||||
|
||||
// Forward declarations
|
||||
static void shaper_timer_cb(void* arg); // Shaper wait callback
|
||||
|
||||
static void shaper_arm(struct ETCP_LINK* link) {
|
||||
uint64_t now = get_time_tb();
|
||||
if (link->shaper_timer || !link->bbr_pacing_rate || link->shaper_load_time_tb < now + SHAPER_BURST_DELAY_TB) return;
|
||||
uint64_t delay = link->shaper_load_time_tb - now + !!link->shaper_sub_nanotime;
|
||||
if (delay > INT_MAX) delay = INT_MAX;
|
||||
link->shaper_timer = uasync_set_timeout(link->etcp->instance->ua, (int)delay, link, shaper_timer_cb, "etcp_shaper");
|
||||
if (!link->shaper_timer) {
|
||||
struct ETCP_CONN* conn = link->etcp;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot schedule pacing link=%u rate_Bps=%u delay_tb=%llu",
|
||||
conn->log_name, link->local_link_id, link->bbr_pacing_rate, (unsigned long long)delay);
|
||||
conn->callbacks_running++; etcp_connection_close(conn); conn->callbacks_running--;
|
||||
}
|
||||
}
|
||||
|
||||
void etcp_loadbalancer_set_pacing_rate(struct ETCP_LINK* link, uint32_t rate) {
|
||||
if (!link || !link->etcp) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pacing: invalid link=%p", (void*)link); return; }
|
||||
uint32_t old_rate = link->bbr_pacing_rate;
|
||||
if (rate == old_rate) return;
|
||||
uint64_t now = get_time_tb(), debt = 0;
|
||||
int was_blocked = link->shaper_timer != NULL;
|
||||
if (old_rate && rate && link->shaper_load_time_tb >= now) {
|
||||
debt = (link->shaper_load_time_tb - now) * SUB_MODULO + link->shaper_sub_nanotime;
|
||||
// debt*old_rate/rate без переполнения: остаток и обе ставки не шире uint32.
|
||||
uint64_t quotient = debt / rate, product = (debt % rate) * old_rate;
|
||||
uint64_t tail = product / rate + !!(product % rate);
|
||||
if (quotient > (UINT64_MAX - tail) / old_rate) debt = UINT64_MAX;
|
||||
else debt = quotient * old_rate + tail;
|
||||
}
|
||||
if (link->shaper_timer) {
|
||||
uasync_cancel_timeout(link->etcp->instance->ua, link->shaper_timer); link->shaper_timer = NULL;
|
||||
}
|
||||
link->bbr_pacing_rate = rate;
|
||||
link->bandwidth = (uint32_t)(((uint64_t)rate * 8 + 999) / 1000);
|
||||
link->shaper_load_time_tb = now + debt / SUB_MODULO;
|
||||
link->shaper_sub_nanotime = debt % SUB_MODULO;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] pacing link=%u rate_Bps=%u->%u debt_sub=%llu deadline_tb=%llu now_tb=%llu",
|
||||
link->etcp->log_name, link->local_link_id, old_rate, rate, (unsigned long long)debt,
|
||||
(unsigned long long)link->shaper_load_time_tb, (unsigned long long)now);
|
||||
shaper_arm(link);
|
||||
if (was_blocked && !link->shaper_timer) etcp_conn_wake_sender(link->etcp);
|
||||
}
|
||||
|
||||
// Минимум inflight среди вмещающих целый DATA; при равенстве начинаем после прошлого выбранного линка.
|
||||
struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp, uint32_t data_len) {
|
||||
if (!etcp || !etcp->links) return NULL;
|
||||
@@ -71,16 +112,16 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] ACK/control bypasses DATA pacer link=%u", etcp->log_name, link->local_link_id);
|
||||
} else if (link->burst_active) {
|
||||
// burst bypasses shaper — не обновляем нагрузку
|
||||
} else if (link->bandwidth == 0) {
|
||||
} else if (link->bbr_pacing_rate == 0) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] unlimited bandwidth, skipping shaper update", etcp->log_name);
|
||||
// Don't return here, let the function continue to free dgram at the end
|
||||
} else {
|
||||
// Time to transmit (ns per byte = 8e6 / bw for Kbits/sec)
|
||||
double byte_time_ns = 8000000.0 / (double)link->bandwidth;
|
||||
uint64_t tx_time_ns = (uint64_t)(pkt_size * byte_time_ns + 0.5); // Round nearest
|
||||
|
||||
// To sub_nanotime (*10 for 0.1ns)
|
||||
uint64_t tx_sub = tx_time_ns * 10;
|
||||
uint64_t now_tb = get_time_tb();
|
||||
if (link->shaper_load_time_tb < now_tb) {
|
||||
link->shaper_load_time_tb = now_tb; link->shaper_sub_nanotime = 0;
|
||||
}
|
||||
// Ставка B/s; SUB_MODULO соответствует 0.1мс, один sub равен 0.1нс.
|
||||
uint64_t tx_sub = ((uint64_t)pkt_size * 10000000000ULL + link->bbr_pacing_rate - 1) / link->bbr_pacing_rate;
|
||||
|
||||
// Add to load
|
||||
link->shaper_sub_nanotime += tx_sub;
|
||||
@@ -88,17 +129,7 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) {
|
||||
link->shaper_sub_nanotime -= carry * SUB_MODULO;
|
||||
link->shaper_load_time_tb += carry;
|
||||
|
||||
// Check if exceeds burst -> schedule timer
|
||||
uint64_t now_tb=get_time_tb();
|
||||
if (link->shaper_load_time_tb >= now_tb + SHAPER_BURST_DELAY_TB) {
|
||||
uint64_t wait_tb = link->shaper_load_time_tb - now_tb;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] scheduling shaper timer (wait_tb=%llu) BW=%d pkt_size=%d", etcp->log_name, (unsigned long long)wait_tb, link->bandwidth, pkt_size);
|
||||
link->shaper_timer = uasync_set_timeout(link->etcp->instance->ua, wait_tb, link, shaper_timer_cb, "shaper");
|
||||
if (link->shaper_timer == NULL) DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Filed to allocate timer");
|
||||
}
|
||||
|
||||
// Inactivity correction
|
||||
if (link->shaper_load_time_tb < now_tb-ALLOWED_DELTA) link->shaper_load_time_tb = now_tb-ALLOWED_DELTA;
|
||||
shaper_arm(link);
|
||||
}
|
||||
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] updated load_tb=%llu, sub=%llu, state=%u", etcp->log_name,
|
||||
// (unsigned long long)link->shaper_load_time_tb, (unsigned long long)link->shaper_sub_nanotime, link->shaper_timer==NULL?1:0);
|
||||
@@ -159,6 +190,8 @@ static void shaper_timer_cb(void* arg) {
|
||||
if (!link) return;
|
||||
|
||||
link->shaper_timer = NULL;
|
||||
shaper_arm(link);
|
||||
if (link->shaper_timer || link->etcp->close_requested) return;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "link=%p now ready", link);
|
||||
|
||||
// Notify loadbalancer that link is ready
|
||||
|
||||
@@ -17,6 +17,10 @@ extern "C" {
|
||||
#define DELTA_TIME_NS 10000ULL // Delta for inactivity
|
||||
#define SHAPER_BURST_DELAY_TB 10ULL // 10 = 1ms burst allowance
|
||||
|
||||
/* Применить pacing в B/s, пересчитать оставшийся долг/таймер по новой ставке и разбудить готовый отправитель.
|
||||
* 0 явно отключает pacing; положительная скорость не округляется до Kbit/s. При OOM таймера закрытие отложено. */
|
||||
void etcp_loadbalancer_set_pacing_rate(struct ETCP_LINK* link, uint32_t bytes_per_second);
|
||||
|
||||
/* Заимствованный рабочий линк с минимальным inflight, round-robin при равенстве; NULL — нет подходящего.
|
||||
* data_len > 0 требует места для целого DATA и готовности shaper; 0 выбирает ACK без DATA-cwnd/pacing.
|
||||
* ACK уже ограничен входящим потоком/ACK_DELAY; исходящая оценка DATA не описывает полосу обратных подтверждений.
|
||||
|
||||
Reference in new issue
Block a user