@ -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)