diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e254dda2..f4b623b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ version at the top when it merges. ## Unreleased +- `TCPGROWRTT=` in an interface file sets the handshake round trip + (1 to 65,535 ms) at or above which a TCP receive window on that interface + grows to its maximum. Unset, every device keeps 10 ms. The key adds 100 + bytes, bringing the resident `bsdsocket.library` image to 365,416 bytes; + its budget is 366,000 bytes. - TCP ACK batching can be set per interface with `TCPACKMAX`. WiFiPi defaults to an 11,680-byte ceiling when the setting is absent; other devices retain the 50,176-byte port default. This changes ACK cadence, not the unresolved diff --git a/docs/user/AmiNetXDuo.guide b/docs/user/AmiNetXDuo.guide index 8b148028b..80038275b 100644 --- a/docs/user/AmiNetXDuo.guide +++ b/docs/user/AmiNetXDuo.guide @@ -478,6 +478,16 @@ interface is next attached. This is a TCP stack setting, not a SANA-II driver command. + @{b}TCPGROWRTT@{ub} + Handshake round trip in milliseconds, 1 to 65535, at or + above which a TCP receive window on this interface grows + from its starting size to the largest the socket may + use. Both sizes depend on the packet pool and on window + scaling. Leave it out for 10 on every device. A smaller value grows the window on shorter + paths, including a local network. The value applies to + this interface only; editing the file takes effect when + the interface is next attached. + Interface files written for Roadshow usually carry other keywords: COPYMODE, ALIAS, METRIC, LEASE, DHCPUNICAST, LINKSTATUSCOMMAND, MULTICAST, POINTTOPOINT, DESTINATION and BROADCASTADDRESS among diff --git a/include/aminetxduo/config.h b/include/aminetxduo/config.h index 217a9904c..6b7f3f24d 100644 --- a/include/aminetxduo/config.h +++ b/include/aminetxduo/config.h @@ -55,6 +55,7 @@ extern "C" { #endif #define AMI_CFG_WRITEREQUESTS_MAX AMINETXDUO_TX_SLOTS #define AMI_CFG_TCP_ACK_MAX 65535UL +#define AMI_CFG_TCP_GROW_RTT_MAX 65535UL #define AMI_CFG_PATH_LEN 128 /* The default domain gets its own cap: SetDefaultDomainName()'s autodoc says @@ -203,6 +204,9 @@ typedef struct AmiIfConfig { /* TCPACKMAX: maximum unacknowledged receive bytes before an ACK is requested on connections using this interface. 0 = device default. */ ULONG tcp_ack_max; + /* TCPGROWRTT: handshake round trip in ms at or above which a socket on + this interface grows its receive window. 0 = BSD_TCP_WINDOW_GROW_RTT_MS. */ + UWORD tcp_grow_rtt; BYTE priority; /* PRIORITY=, -128..127, 0 unset: the highest wins a route two interfaces could carry */ diff --git a/include/aminetxduo/config_advice.h b/include/aminetxduo/config_advice.h index fb311cc26..c2e21fb99 100644 --- a/include/aminetxduo/config_advice.h +++ b/include/aminetxduo/config_advice.h @@ -65,5 +65,6 @@ #define AMI_CFG_ADVICE_PRIORITY_IS_A_SMALL 50 #define AMI_CFG_ADVICE_FILTER_IS_LOCAL_IPANDARP 51 #define AMI_CFG_ADVICE_TCPACKMAX_IS_ACK_BYTES 52 +#define AMI_CFG_ADVICE_TCPGROWRTT_IS_MILLISECONDS 53 #endif /* AMINETXDUO_CONFIG_ADVICE_H */ diff --git a/include/aminetxduo/sana2.h b/include/aminetxduo/sana2.h index 91716e013..ce9660da7 100644 --- a/include/aminetxduo/sana2.h +++ b/include/aminetxduo/sana2.h @@ -102,6 +102,9 @@ ULONG ami_sana2_get_bps(const AmiSana2If *iface); /* Bytes the card holds from the wire with nobody draining it, 0 = not stated (aminetxduo/anxs2ext.h, ANXD_CMD_RX_CAPACITY). */ ULONG ami_sana2_get_hw_rx_bytes(const AmiSana2If *iface); +/* The interface file's TCPGROWRTT in ms, 0 = not set (bsdsocket_window.h, + BSD_TCP_WINDOW_GROW_RTT_MS). */ +ULONG ami_sana2_get_tcp_grow_rtt(const AmiSana2If *iface); BOOL ami_sana2_is_online(const AmiSana2If *iface); /* diff --git a/install/Install-AmiNetXDuo b/install/Install-AmiNetXDuo index ff96f111a..b283559f9 100644 --- a/install/Install-AmiNetXDuo +++ b/install/Install-AmiNetXDuo @@ -2240,6 +2240,8 @@ (append "; nothing asks it anything.\n") (append "; TCPACKMAX= caps received TCP bytes between requested ACKs.\n") (append "; Leave it out for this device's default.\n") + (append "; TCPGROWRTT= is the handshake round trip in ms at which a\n") + (append "; TCP receive window grows. Leave it out for 10.\n") (append ";\n") (append "; An IPv6 build also understands CONFIGURE6 (OFF, LINKLOCAL,\n") (append "; AUTO, DHCP or STATIC), ADDRESS6 and GATEWAY6. A build\n") diff --git a/src/bsdsocket/bsdsocket_window.c b/src/bsdsocket/bsdsocket_window.c index 19dcd4f76..63f24315a 100644 --- a/src/bsdsocket/bsdsocket_window.c +++ b/src/bsdsocket/bsdsocket_window.c @@ -75,11 +75,18 @@ ULONG ami_bsd_tcp_window_fit(ULONG window, ULONG hw_bytes, ULONG mss) return (fit < window) ? fit : window; } +/* TCPGROWRTT, or the built-in line where the interface sets none. */ +static ULONG bsd_grow_rtt(ULONG grow_rtt_ms) +{ + return (grow_rtt_ms != 0UL) ? grow_rtt_ms + : (ULONG)BSD_TCP_WINDOW_GROW_RTT_MS; +} + ULONG ami_bsd_tcp_window_settle(ULONG created, ULONG maximum, ULONG bps, - ULONG rtt_ms) + ULONG rtt_ms, ULONG grow_rtt_ms) { /* A long path: the window is the rate, bursts are paced far away. */ - if (rtt_ms >= (ULONG)BSD_TCP_WINDOW_GROW_RTT_MS) + if (rtt_ms >= bsd_grow_rtt(grow_rtt_ms)) return (maximum > created) ? maximum : created; /* A short path on a link that can put the whole window on the wire at @@ -97,9 +104,10 @@ ULONG ami_bsd_tcp_window_settle(ULONG created, ULONG maximum, ULONG bps, return created; } -BOOL ami_bsd_tcp_window_burst_bound(ULONG bps, ULONG rtt_ms) +BOOL ami_bsd_tcp_window_burst_bound(ULONG bps, ULONG rtt_ms, + ULONG grow_rtt_ms) { - if (rtt_ms >= (ULONG)BSD_TCP_WINDOW_GROW_RTT_MS && + if (rtt_ms >= bsd_grow_rtt(grow_rtt_ms) && bps >= (ULONG)BSD_TCP_WINDOW_FAST_BPS) return FALSE; diff --git a/src/bsdsocket/bsdsocket_window.h b/src/bsdsocket/bsdsocket_window.h index fba2597d0..7f38746c4 100644 --- a/src/bsdsocket/bsdsocket_window.h +++ b/src/bsdsocket/bsdsocket_window.h @@ -55,7 +55,8 @@ * pool: 262,144 on a gigabit LAN is the burst the GENET's ring was measured * to take, 1 MB on a long path is 300 Mbit/s at 26 ms (below). The 10 ms * threshold is a coarse but safe line: the real A1200's LAN round trip is - * 1-5 ms, the emulator's under 1, the Internet's 20 and up. + * 1-5 ms, the emulator's under 1, the Internet's 20 and up. An interface + * file's TCPGROWRTT replaces it on that interface alone (#89). */ #ifndef BSD_TCP_WINDOW_LAN #define BSD_TCP_WINDOW_LAN 100352UL /* (512 / 8) * 1568, the old ceiling */ @@ -143,10 +144,11 @@ * The window a socket settles at once its handshake is done. `created` is * what it opened with, `maximum` what it may grow to, `bps` the link it came * up on (0 = unknown), `rtt_ms` the handshake's round trip (0 = not - * measured, which a passive socket cannot). Pure arithmetic, host-tested. + * measured, which a passive socket cannot), `grow_rtt_ms` the interface's + * TCPGROWRTT (0 = BSD_TCP_WINDOW_GROW_RTT_MS). Pure arithmetic, host-tested. */ ULONG ami_bsd_tcp_window_settle(ULONG created, ULONG maximum, ULONG bps, - ULONG rtt_ms); + ULONG rtt_ms, ULONG grow_rtt_ms); /* * The window a card can absorb: `hw_bytes` is what the driver holds from @@ -174,9 +176,11 @@ ULONG ami_bsd_tcp_window_fit(ULONG window, ULONG hw_bytes, ULONG mss); * path is still burst-bound -- the Internet is faster than a 100 Mbit card, * so its bursts arrive at the card's own line rate and a 25 MHz 68030 * behind a 13 KB ring cannot take them (anxs2ext.h, ANXD_CMD_RX_CAPACITY). - * Pure arithmetic, host-tested. + * `grow_rtt_ms` as for ami_bsd_tcp_window_settle(). Pure arithmetic, + * host-tested. */ -BOOL ami_bsd_tcp_window_burst_bound(ULONG bps, ULONG rtt_ms); +BOOL ami_bsd_tcp_window_burst_bound(ULONG bps, ULONG rtt_ms, + ULONG grow_rtt_ms); #ifndef BSD_TCP_WINDOW_CEILING #ifdef AMINETXDUO_TCP_WINDOW_SCALING diff --git a/src/bsdsocket/socket.c b/src/bsdsocket/socket.c index 192b06ebc..bf4e5a106 100644 --- a/src/bsdsocket/socket.c +++ b/src/bsdsocket/socket.c @@ -205,9 +205,10 @@ VOID bsd_tcp_window_settle(NX_TCP_SOCKET *tcp, ULONG rtt_ms) ? (AmiSana2If *)nxif->nx_interface_additional_link_info : NULL; ULONG bps = (sana != NULL) ? ami_sana2_get_bps(sana) : 0UL; + ULONG grow = ami_sana2_get_tcp_grow_rtt(sana); /* 0 = built-in line */ ULONG cur = tcp->nx_tcp_socket_rx_window_default; ULONG want = ami_bsd_tcp_window_settle(cur, bsd_tcp_window_top(tcp), bps, - rtt_ms); + rtt_ms, grow); /* ... and never more than the card behind this interface can hold from the wire at once (bsdsocket_window.h, ami_bsd_tcp_window_fit), where @@ -215,7 +216,7 @@ VOID bsd_tcp_window_settle(NX_TCP_SOCKET *tcp, ULONG rtt_ms) the path (ami_bsd_tcp_window_burst_bound). The segment size is the one the handshake settled: nx_tcp_socket_mss is what the application asked for and stays 0 on an accepted socket. */ - if (sana != NULL && ami_bsd_tcp_window_burst_bound(bps, rtt_ms)) + if (sana != NULL && ami_bsd_tcp_window_burst_bound(bps, rtt_ms, grow)) want = ami_bsd_tcp_window_fit(want, ami_sana2_get_hw_rx_bytes(sana), tcp->nx_tcp_socket_connect_mss); diff --git a/src/config/config_advice.c b/src/config/config_advice.c index 7ee1daf90..122a6ecae 100644 --- a/src/config/config_advice.c +++ b/src/config/config_advice.c @@ -129,6 +129,7 @@ static const char *const ami_cfg_advice_text[] = "TCPACKMAX is the maximum number of received TCP bytes to wait " "before requesting an ACK, 1 to 65535. Leave it out for the " "device default; smaller values send more ACKs.", + "TCPGROWRTT is milliseconds, 1 to 65535. Leave it out for 10.", }; const char *ami_cfg_advice(UWORD code) diff --git a/src/config/config_parse.c b/src/config/config_parse.c index a93680b45..2de5ebd47 100644 --- a/src/config/config_parse.c +++ b/src/config/config_parse.c @@ -44,6 +44,7 @@ typedef enum IF_KEY_WRITEREQUESTS, IF_KEY_RXBUFFER, IF_KEY_TCPACKMAX, + IF_KEY_TCPGROWRTT, IF_KEY_PRIORITY } IfKey; @@ -78,6 +79,7 @@ ami_if_keywords[] = { "writerequests", IF_KEY_WRITEREQUESTS }, { "rxbuffer", IF_KEY_RXBUFFER }, { "tcpackmax", IF_KEY_TCPACKMAX }, + { "tcpgrowrtt", IF_KEY_TCPGROWRTT }, { "priority", IF_KEY_PRIORITY }, /* Roadshow's, and PRI */ { "pri", IF_KEY_PRIORITY }, @@ -780,6 +782,21 @@ LONG ami_cfg_parse_interface(const char *name, char *buf, AmiIfConfig *out) } break; + case IF_KEY_TCPGROWRTT: + if (ami_cfg_parse_ulong(value, &n) && + n != 0 && n <= AMI_CFG_TCP_GROW_RTT_MAX) + { + out->tcp_grow_rtt = (UWORD)n; + } + else + { + AMI_WARN("config: %s: bad TCPGROWRTT '%s'", out->name, value); + report_bad_value(lineno, AMI_CFG_PROBLEM_WARN, + "TCPGROWRTT", value, + AMI_CFG_ADVICE_TCPGROWRTT_IS_MILLISECONDS); + } + break; + case IF_KEY_IPREQUESTS: case IF_KEY_ARPREQUESTS: case IF_KEY_WRITEREQUESTS: diff --git a/src/config/test/test_config.c b/src/config/test/test_config.c index 026a574a5..664b9f730 100644 --- a/src/config/test/test_config.c +++ b/src/config/test/test_config.c @@ -1150,6 +1150,58 @@ static void test_interface_tcp_ack_max(void) ami_config_set_reporter(NULL, NULL); } +static void test_interface_tcp_grow_rtt(void) +{ + AmiIfConfig iface; + char *buf; + + printf("interface: TCPGROWRTT is optional and bounded per interface\n"); + + buf = dup_text("device = anxwifipi.device\nconfigure = dhcp\n"); + CHECK(ami_cfg_parse_interface("wifipi", buf, &iface) == AMI_CFG_OK); + CHECK(iface.tcp_grow_rtt == 0); + free(buf); + + buf = dup_text("device = anxwifipi.device\nconfigure = dhcp\n" + "tcpgrowrtt = 2\n"); + CHECK(ami_cfg_parse_interface("wifipi", buf, &iface) == AMI_CFG_OK); + CHECK(iface.tcp_grow_rtt == 2UL); + free(buf); + + buf = dup_text("device = genet.device\nconfigure = dhcp\n" + "TCPGROWRTT=65535\n"); + CHECK(ami_cfg_parse_interface("eth", buf, &iface) == AMI_CFG_OK); + CHECK(iface.tcp_grow_rtt == 65535UL); + free(buf); + + seen_count = 0; + ami_config_set_reporter(collect, NULL); + ami_cfg_problem_file("DEVS:NetInterfaces/wifipi"); + buf = dup_text("device = anxwifipi.device\nconfigure = dhcp\n" + "tcpgrowrtt = 0\n"); + CHECK(ami_cfg_parse_interface("wifipi", buf, &iface) == AMI_CFG_OK); + CHECK(iface.tcp_grow_rtt == 0); + CHECK(seen_count == 1 && seen[0].line == 3); + free(buf); + + seen_count = 0; + buf = dup_text("device = anxwifipi.device\nconfigure = dhcp\n" + "tcpgrowrtt = 65536\n"); + CHECK(ami_cfg_parse_interface("wifipi", buf, &iface) == AMI_CFG_OK); + CHECK(iface.tcp_grow_rtt == 0); + CHECK(seen_count == 1 && seen[0].line == 3); + free(buf); + + seen_count = 0; + buf = dup_text("device = anxwifipi.device\nconfigure = dhcp\n" + "tcpgrowrtt = fast\n"); + CHECK(ami_cfg_parse_interface("wifipi", buf, &iface) == AMI_CFG_OK); + CHECK(iface.tcp_grow_rtt == 0); + CHECK(seen_count == 1 && seen[0].line == 3); + free(buf); + ami_config_set_reporter(NULL, NULL); +} + static void test_interface_priority(void) { AmiIfConfig iface; @@ -2968,6 +3020,7 @@ int main(int argc, char **argv) test_inert_keywords_are_notes(); test_request_counts_have_ceilings(); test_interface_tcp_ack_max(); + test_interface_tcp_grow_rtt(); test_interface_priority(); test_interface_ipv6_only(); #ifdef AMINETXDUO_IPV6 diff --git a/src/sana2/sana2_device.c b/src/sana2/sana2_device.c index b455b26f5..ad7d56b36 100644 --- a/src/sana2/sana2_device.c +++ b/src/sana2/sana2_device.c @@ -1320,6 +1320,7 @@ AmiSana2If *ami_sana2_open(const AmiIfConfig *cfg, LONG *err) * ask for); then a vendor driver whose hardware is known gets its number * by name. 0 is no cap. */ + iface->tcp_grow_rtt = cfg->tcp_grow_rtt; if (cfg->rx_buffer != 0) iface->hw_rx_bytes = cfg->rx_buffer; else if ((iface->extension.Accepted & ANXD_S2F_RX_CAPACITY) != 0) @@ -1607,6 +1608,11 @@ ULONG ami_sana2_get_hw_rx_bytes(const AmiSana2If *iface) return (iface != NULL) ? iface->hw_rx_bytes : 0; } +ULONG ami_sana2_get_tcp_grow_rtt(const AmiSana2If *iface) +{ + return (iface != NULL) ? iface->tcp_grow_rtt : 0; +} + /* * What a vendor driver's card holds from the wire, by the driver's name, * for the drivers whose hardware is known: they cannot be asked. The diff --git a/src/sana2/sana2_internal.h b/src/sana2/sana2_internal.h index e9ab1cd31..b3568a10c 100644 --- a/src/sana2/sana2_internal.h +++ b/src/sana2/sana2_internal.h @@ -860,6 +860,8 @@ struct AmiSana2If nobody draining it, 0 = not stated. The TCP window a socket on this interface settles at stays inside it (bsdsocket_window.h). */ ULONG hw_rx_bytes; + /* TCPGROWRTT in ms, 0 = BSD_TCP_WINDOW_GROW_RTT_MS (bsdsocket_window.h). */ + UWORD tcp_grow_rtt; /* The interface file's IPREQUESTS, ARPREQUESTS (0 = the plan decides) and WRITEREQUESTS (1..AMI_SANA2_TX_SLOTS: how many of tx[] are diff --git a/tests/netstack/host/test_pool_window_host.c b/tests/netstack/host/test_pool_window_host.c index 7c3c4bbdc..0c56cfec6 100644 --- a/tests/netstack/host/test_pool_window_host.c +++ b/tests/netstack/host/test_pool_window_host.c @@ -460,62 +460,86 @@ static void i_the_window_settles_for_the_path_and_the_link(void) const ULONG rtt = (ULONG)BSD_TCP_WINDOW_GROW_RTT_MS; /* A long path grows to the maximum on any link, even a gigabit one. */ - h_check(ami_bsd_tcp_window_settle(lan, max, gbit, rtt) == max, + h_check(ami_bsd_tcp_window_settle(lan, max, gbit, rtt, 0UL) == max, "a long path on a gigabit link does not grow"); - h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 22UL) == max, + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 22UL, 0UL) == max, "a 22 ms path on 100 Mbit does not grow"); - h_check(ami_bsd_tcp_window_settle(lan, max, 0UL, 95UL) == max, + h_check(ami_bsd_tcp_window_settle(lan, max, 0UL, 95UL, 0UL) == max, "a long path on an unknown link does not grow"); /* ... but never past what it was created with when there is no maximum (a peer with no scaling, a small machine). */ - h_check(ami_bsd_tcp_window_settle(lan, lan, gbit, rtt) == lan, + h_check(ami_bsd_tcp_window_settle(lan, lan, gbit, rtt, 0UL) == lan, "a long path grew past a maximum equal to the created window"); - h_check(ami_bsd_tcp_window_settle(8192UL, 8192UL, 0UL, 300UL) == 8192UL, + h_check(ami_bsd_tcp_window_settle(8192UL, 8192UL, 0UL, 300UL, 0UL) == 8192UL, "a small machine's floor window moved on a long path"); /* A LAN round trip on a gigabit link: the size the burst was measured at -- the driver's ring and 128 posted reads back it (202 Mbit/s at 262,144 against 186 at 65,535, no frame lost, bsdsocket_window.h) -- and no further, whatever the maximum: there the window is one burst. */ - h_check(ami_bsd_tcp_window_settle(lan, max, gbit, rtt - 1UL) == lmax, + h_check(ami_bsd_tcp_window_settle(lan, max, gbit, rtt - 1UL, 0UL) == lmax, "a gigabit LAN socket did not settle at the LAN maximum"); - h_check(ami_bsd_tcp_window_settle(lan, max, gbit, 0UL) == lmax, + h_check(ami_bsd_tcp_window_settle(lan, max, gbit, 0UL, 0UL) == lmax, "a passive gigabit socket did not settle at the LAN maximum"); - h_check(ami_bsd_tcp_window_settle(50176UL, max, gbit, 0UL) == lmax, + h_check(ami_bsd_tcp_window_settle(50176UL, max, gbit, 0UL, 0UL) == lmax, "a small created window on a gigabit LAN did not grow"); - h_check(ami_bsd_tcp_window_settle(lan, 200000UL, gbit, 0UL) == 200000UL, + h_check(ami_bsd_tcp_window_settle(lan, 200000UL, gbit, 0UL, 0UL) == 200000UL, "a gigabit LAN socket grew past a maximum under the LAN maximum"); /* ... while the same socket on a long path takes the whole maximum. */ - h_check(ami_bsd_tcp_window_settle(lan, max, gbit, rtt) == max && + h_check(ami_bsd_tcp_window_settle(lan, max, gbit, rtt, 0UL) == max && max > lmax, "a long path over a gigabit link stopped at the LAN maximum"); /* Whether the card must hold the window: a LAN round trip always, a slower card on any path; a gigabit card on a long path is drained at wire speed into posted reads while the far link paces the data. */ - h_check(ami_bsd_tcp_window_burst_bound(gbit, rtt - 1UL) == TRUE, + h_check(ami_bsd_tcp_window_burst_bound(gbit, rtt - 1UL, 0UL) == TRUE, "a gigabit LAN socket is not burst-bound"); - h_check(ami_bsd_tcp_window_burst_bound(gbit, 0UL) == TRUE, + h_check(ami_bsd_tcp_window_burst_bound(gbit, 0UL, 0UL) == TRUE, "a passive gigabit socket is not burst-bound"); - h_check(ami_bsd_tcp_window_burst_bound(gbit, rtt) == FALSE, + h_check(ami_bsd_tcp_window_burst_bound(gbit, rtt, 0UL) == FALSE, "a gigabit card on a long path is burst-bound"); - h_check(ami_bsd_tcp_window_burst_bound(hundm, 26UL) == TRUE, + h_check(ami_bsd_tcp_window_burst_bound(hundm, 26UL, 0UL) == TRUE, "a 100 Mbit card on a long path is not burst-bound"); - h_check(ami_bsd_tcp_window_burst_bound(tenm, 300UL) == TRUE, + h_check(ami_bsd_tcp_window_burst_bound(tenm, 300UL, 0UL) == TRUE, "a 10 Mbit card on a long path is not burst-bound"); - h_check(ami_bsd_tcp_window_burst_bound(0UL, 95UL) == TRUE, + h_check(ami_bsd_tcp_window_burst_bound(0UL, 95UL, 0UL) == TRUE, "an unknown link on a long path is not burst-bound"); - h_check(ami_bsd_tcp_window_settle(lan, lan, gbit, 0UL) == lan, + h_check(ami_bsd_tcp_window_settle(lan, lan, gbit, 0UL, 0UL) == lan, "a gigabit LAN socket grew past a maximum equal to its window"); /* A LAN round trip on 10 or 100 Mbit: exactly what it opened with. */ - h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 1UL) == lan, + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 1UL, 0UL) == lan, "a 100 Mbit LAN socket did not keep the LAN window"); - h_check(ami_bsd_tcp_window_settle(lan, max, tenm, 5UL) == lan, + h_check(ami_bsd_tcp_window_settle(lan, max, tenm, 5UL, 0UL) == lan, "a 10 Mbit LAN socket did not keep the LAN window"); - h_check(ami_bsd_tcp_window_settle(lan, max, 0UL, 0UL) == lan, + h_check(ami_bsd_tcp_window_settle(lan, max, 0UL, 0UL, 0UL) == lan, "an unknown link on a LAN did not keep the LAN window"); + /* TCPGROWRTT moves the line for its interface alone; 0 is the built-in + 10 ms, which is also what a socket with no interface gets. */ + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 2UL, 2UL) == max, + "TCPGROWRTT=2 did not grow a 2 ms path"); + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 1UL, 2UL) == lan, + "TCPGROWRTT=2 grew a 1 ms path"); + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 2UL, 0UL) == lan, + "the default line grew a 2 ms path"); + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, rtt - 1UL, 0UL) == lan && + ami_bsd_tcp_window_settle(lan, max, hundm, rtt, 0UL) == max, + "an unset TCPGROWRTT is not the 10 ms line"); + h_check(ami_bsd_tcp_window_settle(lan, max, hundm, 22UL, 50UL) == lan, + "TCPGROWRTT=50 grew a 22 ms path"); + h_check(ami_bsd_tcp_window_settle(lan, max, gbit, 2UL, 2UL) == max && + ami_bsd_tcp_window_settle(lan, max, gbit, 2UL, 0UL) == lmax, + "TCPGROWRTT did not lift a gigabit LAN past the LAN maximum"); + h_check(ami_bsd_tcp_window_burst_bound(gbit, 2UL, 2UL) == FALSE && + ami_bsd_tcp_window_burst_bound(gbit, 2UL, 0UL) == TRUE, + "TCPGROWRTT did not move the burst-bound line with it"); + h_check(ami_bsd_tcp_window_burst_bound(gbit, rtt, 50UL) == TRUE, + "a raised TCPGROWRTT left a 10 ms gigabit path unbounded"); + h_check(ami_bsd_tcp_window_burst_bound(hundm, 2UL, 2UL) == TRUE, + "TCPGROWRTT unbounded a 100 Mbit card"); + /* The card's own memory caps whatever settled: an X-Surf 100's 13 KB ring (52 pages) holds eight 1536-byte frames of 1460 payload each; the GENET's 256 KB holds more than the LAN window; a card that says diff --git a/tests/sana2/CMakeLists.txt b/tests/sana2/CMakeLists.txt index 43b06e6b9..8ee227099 100644 --- a/tests/sana2/CMakeLists.txt +++ b/tests/sana2/CMakeLists.txt @@ -250,7 +250,8 @@ add_executable(test_sana2_device host/test_sana2_device_host.c "${CMAKE_SOURCE_DIR}/src/sana2/sana2_device.c" "${CMAKE_SOURCE_DIR}/src/sana2/sana2_policy.c" - "${CMAKE_SOURCE_DIR}/src/sana2/sana2_txmeta.c") + "${CMAKE_SOURCE_DIR}/src/sana2/sana2_txmeta.c" + "${CMAKE_SOURCE_DIR}/src/bsdsocket/bsdsocket_window.c") target_compile_options(test_sana2_device PRIVATE -include "${CMAKE_CURRENT_SOURCE_DIR}/host/shim/host_regargs.h") @@ -264,6 +265,7 @@ target_include_directories(test_sana2_device PRIVATE "${CMAKE_SOURCE_DIR}/src/config/test/shim" "${CMAKE_SOURCE_DIR}/src/sana2" "${CMAKE_SOURCE_DIR}/src/net68k" + "${CMAKE_SOURCE_DIR}/src/bsdsocket" "${CMAKE_SOURCE_DIR}/include" "${CMAKE_SOURCE_DIR}/port/threadx-amiga/inc" "${AMINETXDUO_NETXDUO}/ports/linux/gnu/inc" diff --git a/tests/sana2/host/test_sana2_device_host.c b/tests/sana2/host/test_sana2_device_host.c index 6b898b4ea..b00f3e1a1 100644 --- a/tests/sana2/host/test_sana2_device_host.c +++ b/tests/sana2/host/test_sana2_device_host.c @@ -2,6 +2,7 @@ SPDX-License-Identifier: MIT */ #include "sana2_internal.h" +#include "bsdsocket_window.h" #include "aminetxduo/netstack.h" #include "aminetxduo/netstatus.h" @@ -1457,6 +1458,53 @@ static void case_request_counts(void) (VOID)ami_sana2_close(iface); } +/* TCPGROWRTT reaches the interface at open; unsaid, or no interface at all, + is 0, which bsdsocket reads as the built-in 10 ms. */ +static void case_tcp_grow_rtt(void) +{ + const ULONG lan = (ULONG)BSD_TCP_WINDOW_LAN; + const ULONG max = (ULONG)BSD_TCP_WINDOW_MAX; + AmiSana2If *iface; + LONG err = 0; + + printf(" the interface file's TCPGROWRTT reaches the interface\n"); + + h_config(); + h_cfg.tcp_grow_rtt = 2; + iface = ami_sana2_open(&h_cfg, &err); + h_check(iface != NULL && ami_sana2_get_tcp_grow_rtt(iface) == 2UL, + "TCPGROWRTT=2 is carried to the interface"); + /* What socket.c:bsd_tcp_window_settle does with it. */ + h_check(iface != NULL && + ami_bsd_tcp_window_settle(lan, max, 0UL, 2UL, + ami_sana2_get_tcp_grow_rtt(iface)) == max && + ami_bsd_tcp_window_settle(lan, max, 0UL, 1UL, + ami_sana2_get_tcp_grow_rtt(iface)) == lan, + "TCPGROWRTT=2 grows a 2 ms connect and not a 1 ms one"); + if (iface != NULL) + (VOID)ami_sana2_close(iface); + + h_config(); + iface = ami_sana2_open(&h_cfg, &err); + h_check(iface != NULL && ami_sana2_get_tcp_grow_rtt(iface) == 0, + "an unsaid TCPGROWRTT is 0"); + if (iface != NULL) + (VOID)ami_sana2_close(iface); + + h_check(ami_sana2_get_tcp_grow_rtt(NULL) == 0, + "no interface is 0"); + /* A NULL interface takes the built-in rtt >= 10 line, as socket.c. */ + h_check(ami_bsd_tcp_window_settle(lan, max, 0UL, 9UL, + ami_sana2_get_tcp_grow_rtt(NULL)) == lan && + ami_bsd_tcp_window_settle(lan, max, 0UL, 10UL, + ami_sana2_get_tcp_grow_rtt(NULL)) == max && + ami_bsd_tcp_window_burst_bound(1000000000UL, 9UL, + ami_sana2_get_tcp_grow_rtt(NULL)) == TRUE && + ami_bsd_tcp_window_burst_bound(1000000000UL, 10UL, + ami_sana2_get_tcp_grow_rtt(NULL)) == FALSE, + "a NULL interface keeps the 10 ms line"); +} + /* FILTER=EVERYTHING is SANA2OPF_PROM at OpenDevice(), and nothing else in the file touches the flags. */ static void case_filter_everything(void) @@ -1868,6 +1916,7 @@ int main(void) case_stats_request(); case_keeps_online(); case_request_counts(); + case_tcp_grow_rtt(); case_filter_everything(); case_offline_bounded_sync(); case_offline_bounded_late_reply();