You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

164 lines
7.6 KiB

// etcp_loadbalancer.c - Load Balancer Implementation (based on etcp_protocol.txt)
#include "etcp_loadbalancer.h"
#include "../lib/debug_config.h"
#include "../lib/u_async.h"
#include <stdlib.h>
// Enable comprehensive debug output for loadbalancer module
#define DEBUG_CATEGORY_LOADBALANCER 1
// Forward declaration
static void init_timeout_cb(void* arg);
// Constants
#define TIMEBASE_NS 100000 // 0.1ms = 100us = 100000ns
#define DELTA_TIME_NS 10000 // Example delta
// Internal
static uint64_t get_current_nanotime() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t nanotime = (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "get_current_nanotime: tv_sec=%ld, tv_nsec=%ld, result=%llu",
ts.tv_sec, ts.tv_nsec, (unsigned long long)nanotime);
return nanotime;
}
// Select link for transmission (per spec)
struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: etcp=%p", etcp);
if (!etcp || !etcp->links) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: invalid parameters (etcp=%p, links=%p)",
etcp, etcp ? etcp->links : NULL);
return NULL;
}
struct ETCP_LINK* best = NULL;
uint64_t min_load_time = UINT64_MAX;
uint64_t now_ns = get_current_nanotime();
uint64_t now_tb = now_ns / (TIMEBASE_NS / 10); // To 0.1ms units
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: current time=%llu tb, scanning %d links",
(unsigned long long)now_tb, etcp->links ? 1 : 0);
struct ETCP_LINK* link = etcp->links;
int link_index = 0;
while (link) {
link_index++;
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: evaluating link %d (%p) - initialized=%u, is_server=%u, bandwidth=%u",
link_index, link, link->initialized, link->is_server, link->bandwidth);
if (!link->initialized) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d not initialized, checking if client connection needed",
link_index);
// Initiate connection if client
if (!link->is_server && !link->init_timer) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: initiating client connection for link %d", link_index);
// Send INIT (via etcp_encrypt_send with special dgram)
// Set timer
link->init_timer = uasync_set_timeout(etcp->instance->ua, link->init_timeout, link, init_timeout_cb); // Define cb
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: set init timer for link %d", link_index);
}
link = link->next;
continue;
}
// Update load time if inactive
if (link->last_activity < now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10))) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d inactive, updating last_activity from %llu to %llu",
link_index, (unsigned long long)link->last_activity,
(unsigned long long)(now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10))));
link->last_activity = now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10));
}
// Check if can send (load time < now)
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d last_activity=%llu, now_tb=%llu",
link_index, (unsigned long long)link->last_activity, (unsigned long long)now_tb);
if (link->last_activity < now_tb) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d can send (load_time < now)", link_index);
if (link->last_activity < min_load_time) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d is better candidate (activity=%llu < %llu)",
link_index, (unsigned long long)link->last_activity, (unsigned long long)min_load_time);
min_load_time = link->last_activity;
best = link;
}
} else {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d cannot send yet (load_time >= now)", link_index);
}
link = link->next;
}
if (best) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: selected link %p (activity=%llu)",
best, (unsigned long long)min_load_time);
} else {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: no suitable link found");
}
return best;
}
// Init timeout callback (placeholder)
static void init_timeout_cb(void* arg) {
struct ETCP_LINK* link = (struct ETCP_LINK*)arg;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: link=%p, retry_count=%u, timeout=%u",
link, link->init_retry_count, link->init_timeout);
// Resend INIT, increment retry
link->init_retry_count++;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: incremented retry_count to %u", link->init_retry_count);
if (link->init_retry_count > 5) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "init_timeout_cb: max retries exceeded (%u > 5), closing link", link->init_retry_count);
// Fail
etcp_link_close(link);
return;
}
// Resend...
link->init_timeout *= 2; // Backoff
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: doubled timeout to %u ms", link->init_timeout);
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, init_timeout_cb);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: rescheduled timer");
}
// Bandwidth limit update (called after send)
void etcp_loadbalancer_update_after_send(struct ETCP_LINK* link, size_t pkt_size) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: link=%p, pkt_size=%zu", link, pkt_size);
if (!link) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: called with NULL link");
return;
}
if (link->bandwidth == 0) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: link bandwidth is 0, skipping update");
return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: link bandwidth=%u bits/sec, packet size=%zu bytes",
link->bandwidth, pkt_size);
// Time to transmit (ns)
double byte_time_ns = 1000000000.0 / (link->bandwidth * 8.0); // bits/sec to byte/ns
uint64_t tx_time_ns = (uint64_t)(pkt_size * byte_time_ns);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: byte_time_ns=%.2f, tx_time_ns=%llu",
byte_time_ns, (unsigned long long)tx_time_ns);
// To timebase (0.1ms units)
uint64_t tx_time_tb = tx_time_ns / (TIMEBASE_NS / 10);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: tx_time_tb=%llu", (unsigned long long)tx_time_tb);
// Update last_activity
uint64_t now_tb = get_current_nanotime() / (TIMEBASE_NS / 10);
uint64_t old_activity = link->last_activity;
link->last_activity += tx_time_tb;
if (link->last_activity < now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10))) {
link->last_activity = now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10));
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: updated last_activity from %llu to %llu",
(unsigned long long)old_activity, (unsigned long long)link->last_activity);
}