Stream member files with router backpressure and parallel transfer handles
This commit is contained in:
1 parent
f7e1752fc2
commit
25e2ffeea7
13 files changed
+918
-207
No files matched your search
@@ -60,7 +60,8 @@ static void router_close_finalize(void* arg);
|
||||
static void router_conn_free_queues(struct ETCP_ROUTER_CONN* rconn);
|
||||
static int router_conn_reset(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void free_entry(struct ll_entry* e) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq, uint64_t src_node_id, uint16_t ack_ts);
|
||||
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq,
|
||||
uint64_t src_node_id, uint16_t ack_ts, int receive_busy);
|
||||
static void router_forward_transit(struct UTUN_INSTANCE* inst, struct ll_entry* entry, struct SVC_ROUTE_HDR* hdr);
|
||||
static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn, uint32_t seq, const uint8_t* wire, size_t wire_len);
|
||||
static int router_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_INFLIGHT* inf);
|
||||
@@ -488,6 +489,7 @@ static void router_send_queue_free(void* arg) { queue_free(arg); }
|
||||
// Отменить таймеры/waiter и освободить все очереди rconn (общий код для close/restart).
|
||||
static void router_conn_free_queues(struct ETCP_ROUTER_CONN* rconn) {
|
||||
struct UTUN_INSTANCE* inst = rconn->inst;
|
||||
if (rconn->receive_pump) { uasync_call_soon_cancel(inst->ua, rconn->receive_pump); rconn->receive_pump = NULL; }
|
||||
if (rconn->handshake_timer) { uasync_cancel_timeout(inst->ua, rconn->handshake_timer); rconn->handshake_timer = NULL; }
|
||||
if (rconn->ack_timer) { uasync_cancel_timeout(inst->ua, rconn->ack_timer); rconn->ack_timer = NULL; }
|
||||
if (rconn->idle_ack_timer) { uasync_cancel_timeout(inst->ua, rconn->idle_ack_timer); rconn->idle_ack_timer = NULL; }
|
||||
@@ -538,6 +540,8 @@ static int router_conn_reset(struct ETCP_ROUTER_CONN* rconn) {
|
||||
queue_set_callback(rconn->incoming_q, router_incoming_q_cb, rconn);
|
||||
queue_set_threshold(rconn->incoming_q, 0, 0);
|
||||
rconn->incoming_data_ready = 0;
|
||||
rconn->receive_blocked = 0;
|
||||
rconn->peer_receive_blocked = 0;
|
||||
rconn->recv_conn = NULL;
|
||||
|
||||
rconn->rtt = 0; rconn->rtt_jitter = 0;
|
||||
@@ -605,6 +609,7 @@ static void router_send_kick(struct ETCP_ROUTER_CONN* rconn) {
|
||||
return;
|
||||
}
|
||||
int retry = rconn->retrans_seq != rconn->retrans_end;
|
||||
if (rconn->peer_receive_blocked && !retry) return;
|
||||
if (!retry && queue_entry_count(rconn->send_q) == 0) return;
|
||||
if (!retry && queue_entry_count(rconn->inflight_q) >= (int)router_effective_max_inflight(rconn)) {
|
||||
rconn->send_blocked = 1;
|
||||
@@ -638,6 +643,7 @@ static void router_send_drain_cb(struct ll_queue* q, void* arg) {
|
||||
router_send_kick(rconn);
|
||||
return;
|
||||
}
|
||||
if (rconn->peer_receive_blocked) return;
|
||||
if (queue_entry_count(rconn->inflight_q) >= (int)router_effective_max_inflight(rconn)) {
|
||||
rconn->send_blocked = 1;
|
||||
return;
|
||||
@@ -960,7 +966,7 @@ static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) {
|
||||
hdr->src_node_id = inst->node_id;
|
||||
hdr->seq = rconn->rx_seq;
|
||||
hdr->svc_id = rconn->svc_id;
|
||||
hdr->flags = 0;
|
||||
hdr->flags = rconn->receive_blocked ? ROUTER_FLAG_RECEIVE_BUSY : 0;
|
||||
hdr->reset_id = rconn->reset_id;
|
||||
hdr->peer_reset_id = rconn->peer_reset_id;
|
||||
hdr->challenge = 0;
|
||||
@@ -998,7 +1004,7 @@ static void router_schedule_ack(struct ETCP_ROUTER_CONN* rconn) {
|
||||
uasync_cancel_timeout(rconn->inst->ua, rconn->idle_ack_timer);
|
||||
rconn->idle_ack_timer = NULL;
|
||||
}
|
||||
if (rconn->rx_seq == rconn->last_sent_ack_seq) return;
|
||||
if (rconn->rx_seq == rconn->last_sent_ack_seq && !rconn->receive_blocked) return;
|
||||
uint64_t now = get_time_tb();
|
||||
uint64_t elapsed = now - rconn->last_ack_sent_tb;
|
||||
if (elapsed >= ROUTER_ACK_INTERVAL_TB) {
|
||||
@@ -1015,12 +1021,12 @@ static void router_ack_timer_cb(void* arg) {
|
||||
struct ETCP_ROUTER_CONN* rconn = (struct ETCP_ROUTER_CONN*)arg;
|
||||
if (rconn->closed) return;
|
||||
rconn->ack_timer = NULL;
|
||||
if (rconn->rx_seq != rconn->last_sent_ack_seq)
|
||||
if (rconn->rx_seq != rconn->last_sent_ack_seq || rconn->receive_blocked)
|
||||
router_ack_do_send(rconn);
|
||||
if (rconn->rx_seq != rconn->last_sent_ack_seq) {
|
||||
if (rconn->rx_seq != rconn->last_sent_ack_seq || rconn->receive_blocked) {
|
||||
if (!rconn->ack_timer)
|
||||
rconn->ack_timer = uasync_set_timeout(rconn->inst->ua,
|
||||
ROUTER_ACK_INTERVAL_TB, rconn, router_ack_timer_cb, "router_ack");
|
||||
rconn->receive_blocked ? ROUTER_ACK_BUSY_TB : ROUTER_ACK_INTERVAL_TB, rconn, router_ack_timer_cb, "router_ack");
|
||||
} else if (!rconn->idle_ack_timer) {
|
||||
rconn->idle_ack_timer = uasync_set_timeout(rconn->inst->ua,
|
||||
ROUTER_ACK_IDLE_TB, rconn, router_idle_ack_timer_cb, "router_idle_ack");
|
||||
@@ -1105,6 +1111,21 @@ static void router_try_assembly(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN
|
||||
while (1) {
|
||||
struct ll_entry* entry = queue_find_data_by_index(rconn->recv_q, &next);
|
||||
if (!entry) break;
|
||||
int (*ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t) =
|
||||
rconn->inst->router_bindings.receive_ready[rconn->svc_id];
|
||||
if (ready && !ready(rconn->inst, rconn->group_id, rconn->remote_node_id,
|
||||
entry->dgram + SVC_ROUTE_HDR_SIZE, entry->len - SVC_ROUTE_HDR_SIZE)) {
|
||||
if (!rconn->receive_blocked) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
|
||||
"router receive paused: svc=%u peer=%llx next_seq=%u buffered=%u", rconn->svc_id,
|
||||
(unsigned long long)rconn->remote_node_id, next, rconn->recv_q->count);
|
||||
rconn->receive_blocked = 1;
|
||||
if (!rconn->ack_timer) rconn->ack_timer = uasync_set_timeout(rconn->inst->ua,
|
||||
ROUTER_ACK_BUSY_TB, rconn, router_ack_timer_cb, "router_receive_busy");
|
||||
return;
|
||||
}
|
||||
if (rconn->receive_blocked) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
|
||||
"router receive resumed: svc=%u peer=%llx next_seq=%u", rconn->svc_id, (unsigned long long)rconn->remote_node_id, next);
|
||||
rconn->receive_blocked = 0;
|
||||
queue_remove_data(rconn->recv_q, entry);
|
||||
uint64_t peer_id = rconn->peer_reset_id;
|
||||
int ret = router_deliver(rconn, conn ? conn : loopback_conn(rconn->inst), entry->dgram, entry->len);
|
||||
@@ -1156,7 +1177,7 @@ static void router_incoming_q_cb(struct ll_queue* q, void* arg) {
|
||||
// Обработать ACK: обновить RTT/jitter, снять подтверждённые inflight-пакеты, при необходимости
|
||||
// возобновить отправку (снять send_blocked).
|
||||
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn,
|
||||
uint32_t seq, uint64_t src_node_id, uint16_t ack_ts) {
|
||||
uint32_t seq, uint64_t src_node_id, uint16_t ack_ts, int receive_busy) {
|
||||
(void)src_node_id;
|
||||
if (!rconn) return;
|
||||
if ((int32_t)(seq - rconn->tx_sent) > 0) {
|
||||
@@ -1165,6 +1186,17 @@ static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CON
|
||||
rconn->c_stale_ack++;
|
||||
return;
|
||||
}
|
||||
if ((int32_t)(seq - rconn->tx_acked) < 0) { rconn->c_stale_ack++; return; }
|
||||
if (receive_busy != rconn->peer_receive_blocked) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
|
||||
"router peer receive busy=%d svc=%u peer=%llx ack=%u", receive_busy, rconn->svc_id, (unsigned long long)src_node_id, seq);
|
||||
int resumed = rconn->peer_receive_blocked && !receive_busy;
|
||||
rconn->peer_receive_blocked = (uint8_t)receive_busy;
|
||||
if (receive_busy) {
|
||||
rconn->last_ack_changed_tb = get_time_tb(); rconn->no_ack_count = 0;
|
||||
rconn->retrans_seq = rconn->retrans_end;
|
||||
router_cancel_send_waiter(rconn);
|
||||
}
|
||||
if (resumed) router_send_kick(rconn);
|
||||
if ((int32_t)(seq - rconn->tx_acked) <= 0) { rconn->c_stale_ack++; return; }
|
||||
if (ack_ts != 0) {
|
||||
uint16_t new_rtt = get_current_timestamp() - ack_ts;
|
||||
@@ -1276,6 +1308,12 @@ static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETC
|
||||
rconn->c_dup_dropped++; return;
|
||||
}
|
||||
|
||||
if (rconn->incoming_q->count + rconn->recv_q->count >= (unsigned)rconn->max_inflight + 1) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router receive window full: svc=%u peer=%llx next=%u seq=%u buffered=%u",
|
||||
rconn->svc_id, (unsigned long long)rconn->remote_node_id, rconn->rx_seq, seq,
|
||||
rconn->incoming_q->count + rconn->recv_q->count);
|
||||
return;
|
||||
}
|
||||
struct ll_entry* qe = queue_entry_new(4);
|
||||
if (!qe) return;
|
||||
*(uint32_t*)qe->data = seq;
|
||||
@@ -1331,7 +1369,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
size_t payload_len = entry->len - SVC_ROUTE_HDR_SIZE;
|
||||
if (!h->reset_id || (payload_len && (control || h->challenge))
|
||||
|| (!payload_len && control != 0 && control != ROUTER_FLAG_START && control != ROUTER_FLAG_RST
|
||||
&& control != (ROUTER_FLAG_START | ROUTER_FLAG_RST) && control != ROUTER_FLAG_CLOSE)) {
|
||||
&& control != (ROUTER_FLAG_START | ROUTER_FLAG_RST) && control != ROUTER_FLAG_CLOSE && control != ROUTER_FLAG_RECEIVE_BUSY)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router recv: malformed flags=%02x seq=%u len=%zu epoch=%016llx",
|
||||
h->flags, h->seq, payload_len, (unsigned long long)h->reset_id);
|
||||
free_entry(entry); return;
|
||||
@@ -1362,7 +1400,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
} else if (!payload_len) {
|
||||
MF_ROUTE_DEBUG(h->svc_id, "ACK rx peer=%llx next_seq=%u old_acked=%u sent=%u inflight=%d",
|
||||
(unsigned long long)h->src_node_id, h->seq, rconn->tx_acked, rconn->tx_sent, queue_entry_count(rconn->inflight_q));
|
||||
if (!h->challenge) router_handle_ack(inst, rconn, h->seq, h->src_node_id, h->timestamp);
|
||||
if (!h->challenge) router_handle_ack(inst, rconn, h->seq, h->src_node_id, h->timestamp, control == ROUTER_FLAG_RECEIVE_BUSY);
|
||||
} else {
|
||||
rconn->last_recv_pkt_ts = h->timestamp;
|
||||
rconn->last_recv_pkt_local_tb = get_time_tb(); rconn->last_recv_updated = 1;
|
||||
@@ -1436,10 +1474,33 @@ int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id) {
|
||||
return -1;
|
||||
}
|
||||
inst->router_bindings.callbacks[svc_id] = NULL;
|
||||
inst->router_bindings.receive_ready[svc_id] = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "svc_id=%u", svc_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void etcp_router_set_receive_ready(struct UTUN_INSTANCE* inst, uint8_t svc_id,
|
||||
int (*ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t)) {
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router receive readiness: NULL instance"); return; }
|
||||
inst->router_bindings.receive_ready[svc_id] = ready;
|
||||
}
|
||||
|
||||
static void router_receive_pump(void* arg) {
|
||||
struct ETCP_ROUTER_CONN* rconn = arg;
|
||||
rconn->receive_pump = NULL;
|
||||
if (rconn->closed) return;
|
||||
// Физический recv_conn мог исчезнуть за время дискового I/O; сервис получает стабильный instance.
|
||||
router_try_assembly(rconn, loopback_conn(rconn->inst));
|
||||
if (!rconn->closed) router_schedule_ack(rconn);
|
||||
}
|
||||
|
||||
void etcp_router_resume_receive(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, uint8_t svc_id) {
|
||||
struct ETCP_ROUTER_CONN* rconn = inst ? router_conn_find(inst, group, peer, svc_id) : NULL;
|
||||
if (!rconn || rconn->closed || !rconn->receive_blocked || rconn->receive_pump) return;
|
||||
rconn->receive_pump = uasync_call_soon(inst->ua, rconn, router_receive_pump);
|
||||
if (!rconn->receive_pump) DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router receive resume allocation failed svc=%u", svc_id);
|
||||
}
|
||||
|
||||
// Отправить сервисный пакет. entry->dgram[0] = svc_id, остальное — payload. Берёт на себя
|
||||
// освобождение entry в любом исходе. dst==self — loopback-доставка.
|
||||
int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id, struct ll_entry* entry, int force, int mode) {
|
||||
|
||||
@@ -77,6 +77,7 @@ struct SVC_ROUTE_HDR {
|
||||
#define ROUTER_FLAG_ENCRYPTED 0x04 // payload зашифрован AES-128-CCM (nonce+tag+crc)
|
||||
#define ROUTER_FLAG_SIGNED 0x08 // пакет содержит Ed25519 подпись (64 байта после payload)
|
||||
#define ROUTER_FLAG_CLOSE 0x02 // нормальное закрытие conn
|
||||
#define ROUTER_FLAG_RECEIVE_BUSY 0x01 // ACK: сервис приостановил приём, seq не подтверждает отложенные пакеты
|
||||
|
||||
// ====================================================================
|
||||
// 2. Внутренние структуры и константы (etcp_router.c, control_server.c)
|
||||
@@ -164,6 +165,9 @@ struct ETCP_ROUTER_CONN {
|
||||
// Очередь приёма входящего трафика (между сетью и recv_q)
|
||||
struct ll_queue* incoming_q; // FIFO очередь входящих пакетов
|
||||
uint8_t incoming_data_ready; // 1 = данные в incoming_q и callback был вызван
|
||||
uint8_t receive_blocked; // сервис временно не принимает следующую кодограмму
|
||||
uint8_t peer_receive_blocked; // последний ACK пира объявил паузу приёма
|
||||
void* receive_pump; // отложенное продолжение приёма
|
||||
|
||||
uint32_t c_pkts_sent; // успешные отправки данных
|
||||
uint32_t c_pkts_send_err; // ошибки отправки
|
||||
@@ -182,6 +186,7 @@ struct ETCP_ROUTER_CONN {
|
||||
#define ROUTER_MAX_INFLIGHT 256 // макс пакетов в полёте (для контроля inflight)
|
||||
#define ROUTER_ACK_INTERVAL_TB 100 // интервал ACK: 10ms в timebase (0.1ms)
|
||||
#define ROUTER_ACK_IDLE_TB 5000 // idle таймаут: 500ms
|
||||
#define ROUTER_ACK_BUSY_TB 1000 // 100ms: живой приёмник ждёт диск, а не потерял транспорт
|
||||
#define ROUTER_MAX_SEND_Q_PACKETS 64 // порог backpressure на send_q
|
||||
#define ROUTER_SEND_WATCHDOG_TB 5000 // DEBUG-watchdog: 500ms проверка инварианта drain
|
||||
|
||||
@@ -212,6 +217,7 @@ struct ROUTER_INFLIGHT {
|
||||
// Bindings для сервисов внутри etcp_router (аналогично ETCP_BINDINGS)
|
||||
struct ETCP_ROUTER_BINDINGS {
|
||||
etcp_recv_fn callbacks[SVC_ROUTE_MAX_BINDINGS];
|
||||
int (*receive_ready[SVC_ROUTE_MAX_BINDINGS])(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t);
|
||||
};
|
||||
|
||||
// ====================================================================
|
||||
@@ -229,6 +235,13 @@ int etcp_router_bind(struct UTUN_INSTANCE* inst, uint8_t svc_id, etcp_recv_fn c
|
||||
// Снять обработчик; само по себе не закрывает логические каналы сервиса.
|
||||
int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id);
|
||||
|
||||
// Проверяется до доставки/ACK. 0 оставляет пакет в recv_q, сохраняя порядок; CLOSE не блокируется.
|
||||
// ready получает заимствованный проверенный payload и работает в uasync без I/O. NULL снимает ограничение.
|
||||
void etcp_router_set_receive_ready(struct UTUN_INSTANCE* inst, uint8_t svc_id,
|
||||
int (*ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t));
|
||||
// Продолжение отложено; канал не создаётся, waiter/arg сервиса не сохраняются.
|
||||
void etcp_router_resume_receive(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, uint8_t svc_id);
|
||||
|
||||
// Отправить сервисный пакет (авто-conn, seq, inflight-контроль через send_q).
|
||||
// Принимает владение entry во всех исходах, включая ошибку.
|
||||
// entry->dgram = svc_id[1] || payload; 0=принято в очередь, <0=ошибка, не подтверждение доставки.
|
||||
|
||||
Reference in new issue
Block a user