Skip to content

Commit

Permalink
Merge pull request #15953 from miri64/congure/feat/congure_reno
Browse files Browse the repository at this point in the history
congure_reno: initial import of TCP Reno congestion control
  • Loading branch information
benpicco authored Apr 9, 2022
2 parents 4c125db + 3c05f72 commit c89f6bf
Show file tree
Hide file tree
Showing 18 changed files with 1,869 additions and 0 deletions.
8 changes: 8 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ ifneq (,$(filter congure_test,$(USEMODULE)))
USEMODULE += fmt
endif

ifneq (,$(filter congure_reno,$(USEMODULE)))
USEMODULE += congure_reno_methods
endif

ifneq (,$(filter congure_reno_methods,$(USEMODULE)))
USEMODULE += seq
endif

ifneq (,$(filter eepreg,$(USEMODULE)))
FEATURES_REQUIRED += periph_eeprom
endif
Expand Down
2 changes: 2 additions & 0 deletions sys/congure/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ menu "CongURE congestion control abstraction"
depends on USEMODULE_CONGURE

rsource "mock/Kconfig"
rsource "reno/Kconfig"
rsource "test/Kconfig"

endmenu # CongURE congestion control abstraction
Expand All @@ -22,6 +23,7 @@ menuconfig MODULE_CONGURE
if MODULE_CONGURE

rsource "mock/Kconfig"
rsource "reno/Kconfig"
rsource "test/Kconfig"

endif # MODULE_CONGURE
Expand Down
6 changes: 6 additions & 0 deletions sys/congure/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
ifneq (,$(filter congure_mock,$(USEMODULE)))
DIRS += mock
endif
ifneq (,$(filter congure_reno,$(USEMODULE)))
DIRS += reno
endif
ifneq (,$(filter congure_reno_methods,$(USEMODULE)))
DIRS += reno/methods
endif
ifneq (,$(filter congure_test,$(USEMODULE)))
DIRS += test
endif
Expand Down
14 changes: 14 additions & 0 deletions sys/congure/reno/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
config MODULE_CONGURE_RENO
bool "CongURE implementation of TCP Reno"
depends on MODULE_CONGURE
select MODULE_CONGURE_RENO_METHODS

config MODULE_CONGURE_RENO_METHODS
bool "Send driver methods for the CongURE implementation of TCP Reno"
depends on MODULE_SEQ
help
Many other congestion control mechanisms are just adaptations of TCP
Reno, so this makes the methods of @ref sys_congure_reno available to
other @ref sys_congure modules. Use module `congure_reno_methods` to
only compile in these modules, but not the driver for
`congure_reno_snd_t` or @ref congure_reno_snd_setup().
3 changes: 3 additions & 0 deletions sys/congure/reno/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE := congure_reno

include $(RIOTBASE)/Makefile.base
36 changes: 36 additions & 0 deletions sys/congure/reno/congure_reno.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2021 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/

#include "congure/reno.h"

static const congure_snd_driver_t _driver = {
.init = congure_reno_snd_init,
.inter_msg_interval = congure_reno_snd_inter_msg_interval,
.report_msg_sent = congure_reno_snd_report_msg_sent,
.report_msg_discarded = congure_reno_snd_report_msg_discarded,
.report_msgs_timeout = congure_reno_snd_report_msgs_timeout,
.report_msgs_lost = congure_reno_snd_report_msgs_lost,
.report_msg_acked = congure_reno_snd_report_msg_acked,
.report_ecn_ce = congure_reno_snd_report_ecn_ce,
};

void congure_reno_snd_setup(congure_reno_snd_t *c,
const congure_reno_snd_consts_t *consts)
{
c->super.driver = &_driver;
c->consts = consts;
}

/** @} */
3 changes: 3 additions & 0 deletions sys/congure/reno/methods/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE := congure_reno_methods

include $(RIOTBASE)/Makefile.base
248 changes: 248 additions & 0 deletions sys/congure/reno/methods/congure_reno_methods.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/*
* Copyright (C) 2021 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/

#include <assert.h>
#include <stdint.h>

#include "clist.h"
#include "seq.h"

#include "congure/reno.h"

static int _snd_in_fast_retransmit(congure_snd_t *cong)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

return (c->dup_acks >= c->consts->frthresh);
}

static inline congure_wnd_size_t _calc_init_wnd(congure_reno_snd_t *c)
{
/* see https://tools.ietf.org/html/rfc5681#section-3.1 */
if (c->mss > c->consts->cwnd_upper) {
return 2 * c->mss;
}
else if (c->mss <= c->consts->cwnd_lower) {
return 4 * c->mss;
}
else {
return 3 * c->mss;
}
}

static void _fr_cwnd_dec(congure_reno_snd_t *c)
{
if (c->consts->fr_cwnd_dec) {
c->consts->fr_cwnd_dec(c);
}
else {
/* max(c->mss * 2, c->super.cwnd / 2) */
c->ssthresh = ((c->mss * 4) > c->super.cwnd)
? (c->mss * 2) : (c->super.cwnd / 2);
c->super.cwnd = c->ssthresh + (3 * c->mss);
}
}

static void _enforce_fast_retransmit(congure_reno_snd_t *c)
{
if (!_snd_in_fast_retransmit(&c->super)) {
c->dup_acks = c->consts->frthresh;
}
_fr_cwnd_dec(c);
c->consts->fr(c);
}

static void _dec_flight_size(congure_reno_snd_t *c, unsigned msg_size)
{
/* check for integer underflow */
if ((c->in_flight_size - msg_size) > c->in_flight_size) {
c->in_flight_size = 0U;
}
else {
c->in_flight_size -= msg_size;
}
}

void congure_reno_set_mss(congure_reno_snd_t *c, congure_wnd_size_t mss)
{
c->mss = mss;
c->super.cwnd = _calc_init_wnd(c);
}

void congure_reno_snd_init(congure_snd_t *cong, void *ctx)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

c->super.ctx = ctx;
c->mss = c->consts->init_mss;
c->last_ack = UINT32_MAX;
c->super.cwnd = _calc_init_wnd(c);
c->ssthresh = c->consts->init_ssthresh;
c->dup_acks = 0;
}

int32_t congure_reno_snd_inter_msg_interval(congure_snd_t *cong,
unsigned msg_size)
{
(void)cong;
(void)msg_size;
return -1;
}

void congure_reno_snd_report_msg_sent(congure_snd_t *cong, unsigned sent_size)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

if ((c->in_flight_size + sent_size) < c->super.cwnd) {
c->in_flight_size += sent_size;
}
else {
/* state machine is dependent on flight size being smaller or equal
* to cwnd as such cap cwnd here, in case caller reports a message in
* flight that was marked as lost, but the caller is using a later
* message to send another ACK. */
c->in_flight_size = c->super.cwnd;
}
}

void congure_reno_snd_report_msg_discarded(congure_snd_t *cong,
unsigned msg_size)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

assert(msg_size <= c->in_flight_size);

_dec_flight_size(c, msg_size);
}

int _check_resends(clist_node_t *node, void *ctx)
{
congure_snd_msg_t *msg = (congure_snd_msg_t *)node;

(void)ctx;
if (msg->resends == 0) {
return 1;
}
return 0;
}

int _mark_msg_lost(clist_node_t *node, void *ctx)
{
congure_snd_msg_t *msg = (congure_snd_msg_t *)node;
congure_reno_snd_t *c = (void *)ctx;

_dec_flight_size(c, msg->size);
return 0;
}

void congure_reno_snd_report_msgs_timeout(congure_snd_t *cong,
congure_snd_msg_t *msgs)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

if (msgs) {
if (clist_foreach(&msgs->super, _check_resends, NULL)) {
/* see https://tools.ietf.org/html/rfc5681#section-3.1 equation 4 */
c->ssthresh = ((c->in_flight_size / 2) > (c->mss * 2))
? (c->in_flight_size / 2)
: (c->mss * 2);
}
/* do decrementing of flight size _after_ ssthresh reduction,
* since we use the in_flight_size there */
clist_foreach(&msgs->super, _mark_msg_lost, c);
/* > Furthermore, upon a timeout (as specified in [RFC2988]) cwnd
* > MUST be set to no more than the loss window, LW, which equals
* > 1 full-sized segment (regardless of the value of IW). */
c->super.cwnd = c->mss;
}
}

void congure_reno_snd_report_msgs_lost(congure_snd_t *cong,
congure_snd_msg_t *msgs)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

clist_foreach(&msgs->super, _mark_msg_lost, c);
_enforce_fast_retransmit(c);
}

void congure_reno_snd_report_msg_acked(congure_snd_t *cong,
congure_snd_msg_t *msg,
congure_snd_ack_t *ack)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

if (seq32_compare(ack->id, c->last_ack) <= 0) {
/* check for duplicate ACK according to
* https://tools.ietf.org/html/rfc5681#section-2
* An acknowledgment is considered a "duplicate" [...] when
* (a) the receiver of the ACK has outstanding data, */
if ((c->in_flight_size > 0) &&
/* (b) the incoming acknowledgment carries no data, */
(ack->size == 0) &&
/* (c) the SYN and FIN bits are both off */
(ack->clean) &&
/* (d) the acknowledgment number is equal to the greatest
* acknowledgment received on the given connection, and */
(ack->id == c->last_ack) &&
/* (e) the advertised window in the incoming acknowledgment equals
* the advertised window in the last incoming acknowledgment. */
((ack->wnd == 0) || (c->consts->same_wnd_adv(c, ack)))) {
c->dup_acks++;
if (_snd_in_fast_retransmit(cong)) {
_fr_cwnd_dec(c);
c->consts->fr(c);
}
}
}
else {
c->dup_acks = 0;
c->last_ack = ack->id;
if (c->super.cwnd < c->ssthresh) {
/* slow start */
if (c->consts->ss_cwnd_inc) {
c->consts->ss_cwnd_inc(c);
}
else {
c->super.cwnd += (c->in_flight_size < c->mss)
? c->in_flight_size
: c->mss;
}
}
else {
/* congestion avoidance */
if (c->consts->ca_cwnd_inc) {
c->consts->ca_cwnd_inc(c);
}
else {
c->super.cwnd += c->mss;
}
}
assert(msg->size <= c->in_flight_size);
_dec_flight_size(c, msg->size);
}
}

void congure_reno_snd_report_ecn_ce(congure_snd_t *cong, ztimer_now_t time)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

/* see https://tools.ietf.org/html/rfc8311#section-4.1 */
(void)time;
c->super.cwnd /= 2;
c->ssthresh -= c->mss;
}

/** @} */
Loading

0 comments on commit c89f6bf

Please sign in to comment.