From d050aea4cd892d71ed7fc78b6c6149a7231db5fc Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sun, 5 Jul 2026 18:58:37 +0200 Subject: ipcpd: Rework congestion avoidance Congestion avoidance is a property of the layer, orthogonal to ARQ and to flow control: FRCP retransmits and lets the peer pace the sender, per flow, end-to-end; the IPCP paces path aggregates. Each signal means one thing: a loss triggers a retransmission, a mark means congestion, the peer window means a slow receiver. Every flow is paced by the same rate law whatever its QoS, so a greedy raw sender shares a bottleneck fairly with a reliable stream. The unit of control is the (destination address, QoS cube) aggregate: all flows toward that destination share one controller and one rate; a start-time fair-queuing pacer divides the rate across them by deadline instead of blocking the send path, and a new flow rides the aggregate's estimates at its current rate, with no probing of its own. Slow start runs once per aggregate. The congestion signal is a multi-bit magnitude: forwarders mark packets with their standing queue depth, MAX-combined across hops, so a packet carries the deepest queue on its path. The receiver feeds back a time-integral mean over a window that adapts to the flow's byte rate, measuring a slow flow with the same fidelity as a fast one. The sender runs AIMD scaled by elapsed wall-clock time, which makes the steady-state allocation RTT-independent. The PCI gains one byte: the path capacity as a quarter-log2 code. Forwarders estimate their egress rate from busy-period drain and MIN-stamp the byte, the receiver returns the window minimum with its feedback, and the sender scales its rate floor and additive slope to the bottleneck (C / 32). A deep cut implies a backlogged bottleneck and a backlogged bottleneck advertises its capacity, so the scaled floor is live exactly when recovery needs it: the probe heals a halving in seconds at any link rate, and the floor bounds the deepest hole to a factor 32 below the bottleneck. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/ipcpd/unicast/ca/nop.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'src/ipcpd/unicast/ca/nop.c') diff --git a/src/ipcpd/unicast/ca/nop.c b/src/ipcpd/unicast/ca/nop.c index e5cacf66..e6965297 100644 --- a/src/ipcpd/unicast/ca/nop.c +++ b/src/ipcpd/unicast/ca/nop.c @@ -30,8 +30,8 @@ struct ca_ops nop_ca_ops = { .ctx_update_snd = nop_ctx_update_snd, .ctx_update_rcv = nop_ctx_update_rcv, .ctx_update_ece = nop_ctx_update_ece, - .wnd_wait = nop_wnd_wait, .calc_ecn = nop_calc_ecn, + .marks_ecn = false, .print_stats = NULL }; @@ -45,51 +45,52 @@ void nop_ctx_destroy(void * ctx) (void) ctx; } -ca_wnd_t nop_ctx_update_snd(void * ctx, - size_t len) +time_t nop_ctx_update_snd(void * ctx, + size_t len, + uint8_t lecn, + uint64_t * ftag) { - ca_wnd_t wnd; - (void) ctx; (void) len; + (void) lecn; + (void) ftag; - memset(&wnd, 0, sizeof(wnd)); - - return wnd; -} - -void nop_wnd_wait(ca_wnd_t wnd) -{ - (void) wnd; + return 0; } bool nop_ctx_update_rcv(void * ctx, size_t len, uint8_t ecn, - uint16_t * ece) + uint8_t cap, + uint16_t * ece, + uint8_t * fcap) { (void) ctx; (void) len; (void) ecn; + (void) cap; (void) ece; + (void) fcap; return false; } void nop_ctx_update_ece(void * ctx, - uint16_t ece) + uint16_t ece, + uint8_t cap) { (void) ctx; (void) ece; + (void) cap; } -int nop_calc_ecn(int fd, +int nop_calc_ecn(size_t queued, uint8_t * ecn, qoscube_t qc, size_t len) { - (void) fd; + (void) queued; (void) len; (void) ecn; (void) qc; -- cgit v1.2.3