Browse Source

channel select: round-robin and inflight

congestion
Evgeny 5 months ago
parent
commit
6a971389b6
  1. 2
      src/etcp.c
  2. 1
      src/etcp.h
  3. 1
      src/etcp_connections.c
  4. 79
      src/etcp_loadbalancer.c

2
src/etcp.c

@ -97,6 +97,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
etcp->optimal_inflight=100000;
etcp->initialized=0;
etcp->links_up=0;
etcp->last_rr_link=NULL;
etcp->name = u_strdup(name);
// Initialize log_name with local node_id (peer will be updated later when known)
snprintf(etcp->log_name, sizeof(etcp->log_name), "%04X->???? [%s]", (uint16_t)instance->node_id, etcp->name);
@ -259,6 +260,7 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) {
// Reset RTT history
memset(etcp->rtt_history, 0, sizeof(etcp->rtt_history));
etcp->rtt_history_idx = 0;
etcp->last_rr_link = NULL;
// Clear queues (keep queue structures)
clear_queue(etcp->input_queue);

1
src/etcp.h

@ -77,6 +77,7 @@ struct ETCP_CONN {
// Links (channels) - linked list
struct ETCP_LINK* links;
struct ETCP_LINK* last_rr_link; // последний линк, выбранный round-robin
// Crypto and state
struct secure_channel crypto_ctx;

1
src/etcp_connections.c

@ -747,6 +747,7 @@ void etcp_link_close(struct ETCP_LINK* link) {
}
pp = &(*pp)->next;
}
if (link->etcp->last_rr_link == link) link->etcp->last_rr_link = NULL;
remove_link(link->conn, link->ip_port_hash);

79
src/etcp_loadbalancer.c

@ -25,6 +25,7 @@
static void shaper_timer_cb(void* arg); // Shaper wait callback
// Select link for transmission
// Algorithm: min inflight_bytes, round-robin among ties
struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) {
if (!etcp || !etcp->links) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] invalid parameters (etcp=%p, links=%p)",
@ -32,20 +33,13 @@ struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) {
return NULL;
}
struct ETCP_LINK* best = NULL;
uint64_t min_load_tb = UINT64_MAX;
uint64_t now_tb=get_time_tb();
// First pass: find minimum inflight_bytes among available links
uint32_t min_inflight = UINT32_MAX;
int available_count = 0;
struct ETCP_LINK* link = etcp->links;
int link_index = 0;
while (link) {
link_index++;
// DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d (%p) - initialized=%u, state=%u, load_tb=%llu, sub=%llu",
// link_index, link, link->initialized, link->shaper_timer==NULL?1:0,
// (unsigned long long)link->shaper_load_time_tb, (unsigned long long)link->shaper_sub_nanotime);
if (!link->initialized || link->link_status != 1) {// link up/down
if (!link->initialized || link->link_status != 1) {
link = link->next;
continue;
}
@ -54,28 +48,61 @@ struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) {
continue;
}
if (link->shaper_load_time_tb < now_tb-ALLOWED_DELTA) link->shaper_load_time_tb = now_tb-ALLOWED_DELTA;
// Check if ready (load < now + burst allowance)
uint64_t effective_load_ns = link->shaper_load_time_tb * TIMEBASE_NS + (link->shaper_sub_nanotime / 10); // To ns
uint64_t now_ns = now_tb * TIMEBASE_NS;
int64_t delta_t=(int64_t)(now_ns - effective_load_ns);// uint64 может переполняться. чтобы обеспечить правильную цикличность.
if (link->shaper_load_time_tb < min_load_tb || (link->shaper_load_time_tb == min_load_tb && link->shaper_sub_nanotime < best->shaper_sub_nanotime)) {
min_load_tb = link->shaper_load_time_tb;
best = link;
available_count++;
if (link->inflight_bytes < min_inflight) {
min_inflight = link->inflight_bytes;
}
link = link->next;
}
if (best) {
// DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] selected link %p (load_tb=%llu)",
// etcp->log_name, best, (unsigned long long)min_load_tb);
} else {
if (available_count == 0) {
if (etcp->links)
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] no suitable link found: inf:%s, tmr:%s", etcp->log_name, etcp->links->send_blocked_inflight?"wait":"rdy", etcp->links->shaper_timer?"wait":"rdy");
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] no suitable link found: inf:%s, tmr:%s",
etcp->log_name, etcp->links->send_blocked_inflight?"wait":"rdy", etcp->links->shaper_timer?"wait":"rdy");
return NULL;
}
return best;
// Count how many links have the minimum inflight
int min_count = 0;
link = etcp->links;
while (link) {
if (link->initialized && link->link_status == 1 &&
loadbalancer_link_can_send(link) && link->inflight_bytes == min_inflight) {
min_count++;
}
link = link->next;
}
if (min_count == 1) {
// Only one link with minimum inflight - return it directly
link = etcp->links;
while (link) {
if (link->initialized && link->link_status == 1 &&
loadbalancer_link_can_send(link) && link->inflight_bytes == min_inflight) {
return link;
}
link = link->next;
}
}
// Round-robin among links with minimum inflight
// Start from the link after last_rr_link (or from the beginning if last_rr_link is NULL)
struct ETCP_LINK* start = etcp->last_rr_link ? etcp->last_rr_link->next : etcp->links;
if (!start) start = etcp->links;
link = start;
do {
if (link->initialized && link->link_status == 1 &&
loadbalancer_link_can_send(link) && link->inflight_bytes == min_inflight) {
etcp->last_rr_link = link;
return link;
}
link = link->next;
if (!link) link = etcp->links;
} while (link != start);
// Should not reach here, but return NULL just in case
return NULL;
}
// New: Send dgram (select link, encrypt/send, update shaper)

Loading…
Cancel
Save