- Fixed incorrect init_connections() call in test_etcp_simple_traffic.c (was calling server_instance instead of client_instance) - Fixed double free in timeout_heap_pop() when handling deleted elements - Enhanced NULL pointer safety in uasync_print_resources() by removing complex heap manipulation - Added debug logging to timeout_heap_pop() for better error tracking Test results: test_etcp_simple_traffic now passes without double free errors
164 lines
7.6 KiB
C
Executable File
164 lines
7.6 KiB
C
Executable File
// 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);
|
|
} |