Conversation
TCPWINDOWMAX=<bytes> in an interface file is the largest receive window a socket on that interface settles at, 8192..1048576 (BSD_TCP_WINDOW, the smallest window a socket opens with, to BSD_TCP_WINDOW_MAX, the largest it grows to). 0, out-of-range and non-numeric values are warned and ignored like TCPGROWRTT. Unset is 0: no cap, the settle decision is unchanged. The value rides on AmiSana2If beside TCPGROWRTT. The settle decision in socket.c moves into ami_bsd_tcp_window_chosen() (bsdsocket_window.c): _settle(), _fit() where _burst_bound(), then min(want, cap). A NULL interface reads as all zeros, which is the old sana == NULL path: no fit, no cap. The cap only lowers the window, so the scale negotiated at SYN from rx_window_maximum still expresses it. RXBUFFER, the creation window, the maximum and scale negotiation are untouched; SO_RCVBUF still bounds only the receive queue. predict_window (tests/netstack) prints the window a socket settles at for a pool and an interface's keys, from the same arithmetic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SYN / SYN-ACK advertised min(window, 65535) unscaled before settle. A cap under that moves the peer's right edge left, and at scale 4 even 65535 is 65520 on the wire. 65536 >> s << s >= 65535 for every scale up to 5, and 1 MiB negotiates 5, so 65536..1048576 is the range; 65535 is now warned and ignored. The arithmetic stays unfloored. The window tests are split into named cases, one of which checks every in-range cap at scales 0..5 against the handshake window; the config test gets its own case for rejected values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI run 36396529239 at c004ad8 reads bsdsocket.library at 365,536 bytes (image_size spare 464 of 366,000). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#89.
TCPWINDOWMAX=<bytes>in an interface file caps the receive window a TCP socket on that interface settles at. It exists for a one-variable arm: TCPGROWRTT=2 grows a WiFiPi socket to the pool share (802816 on the test A1200), and this cap holds everything else fixed while lowering that figure. RXBUFFER is untouched: it declares card capacity and only feeds the fit when the path is burst-bound.Change
include/aminetxduo/config.h:61-62,216:AMI_CFG_TCP_WINDOW_MAX_MIN65536,_MAX1048576;AmiIfConfig.tcp_window_max(ULONG, 0 = unset).src/config/config_parse.c:802-816: keyTCPWINDOWMAX. 65536..1048576 is accepted; 0, out-of-range and non-numeric values are warned with advice 54 and ignored, following TCPGROWRTT's pattern.src/config/config_advice.c:133: "TCPWINDOWMAX is bytes, 65536 to 1048576. Leave it out for no cap."BSD_TCP_WINDOW_MAX: no socket grows past it, so a larger cap would have no effect.src/sana2/sana2_internal.h:866,sana2_device.c:1324,1617,include/aminetxduo/sana2.h:109: copied atami_sana2_openbeside TCPGROWRTT;ami_sana2_get_tcp_window_max()returns 0 for a NULL interface.src/bsdsocket/bsdsocket_window.c:107,.h:193:ami_bsd_tcp_window_chosen()is the whole settle decision as pure arithmetic:_settle(), then_fit()where_burst_bound(), thenmin(want, cap)when cap != 0.src/bsdsocket/socket.c:205-220:bsd_tcp_window_settlecalls it with the interface's getters. A NULL interface reads as all zeros. That is the oldsana == NULLpath (bps 0, the built-in 10 ms line, and_fit(hw=0)is the identity), so there is no fit and no cap. The shrink/grow application and the scale guard that follow are unchanged.rx_window_maximumand scale negotiation, and SO_RCVBUF.Scale
The scale is negotiated at SYN from
rx_window_maximum(802816, which gives scale 4). The cap only lowerswant, sowant >> scaleis still at most 65535 and the growth guard never fires because of it. At settle nothing has been received, so the existing shrink path removes the whole difference fromrx_window_current. On the wire the window is rounded down to 1 << scale: 65535 advertises as 65520 at scale 4. For a peer that offered no scaling, both fields are pinned at 65535. A cap above that has no effect, and a cap below it shrinks, which needs no scale.SO_RCVBUF
SO_RCVBUF (
options.c:340-369) setsas_RcvBufand, in a low-watermark build, the receive queue maximum in 1460-byte packets. It never writes the window fields, before or after settle.getsockopt(SO_RCVBUF)returns the set value, or the settled (capped)rx_window_defaultwhen unset. Precedence: TCPWINDOWMAX bounds what is advertised; SO_RCVBUF separately bounds what the queue keeps, and a segment past it is dropped even inside the window. A large SO_RCVBUF never lifts the cap.t_rcvbuf_windowintest_sockopt_host.ctests this.Tests (host, playhouse2)
Named cases:
k_tcpwindowmax_unset_and_no_interface_are_baseline,l_tcpwindowmax_clamps_below_and_ignores_above,m_tcpwindowmax_scaled_rounding_at_the_boundaries,n_tcpwindowmax_never_retracts_the_handshake_window(test_pool_window, 1510 checks, 0 failures);test_interface_tcp_window_max,test_interface_tcp_window_max_rejects_bad_values(test_config);case_tcp_window_max(test_sana2_device);t_rcvbuf_window(test_sockopt, test_sockopt_cork).test_pool_window(k-n): unset equals the uncapped decision over 40 path/link/card combinations, and no interface keeps the default line. A cap one below the chosen window clamps it; caps at or above it, including 1048576, change nothing. A cap never raises an ungrown window but can lower one. It is a min with the gigabit LAN maximum and with the X-Surf fit. Scale boundaries: 65535, 65536 and 65535<<4 are tested, and the unscaled peer's pinned 65535 with caps of 65534/65536. A sweep checks that no cap ever exceeds the negotiated maximum, andn_checks that for every cap in 65536..1048576 and every scale 0..5 the advertised window is >= min(created, 65535). A cap of 2000 (below 2*MSS) is applied literally: the arithmetic has no floor, and the parser's 65536 is the floor.test_sana2_devicecase_tcp_window_max: the value reaches the interface, and the socket.c composition caps 802816 to 262144; unset leaves the grown window and NULL is no cap. 427/0.test_configtest_interface_tcp_window_max: absent, 262144, 65536 and 1048576 are accepted; 0, 8192, 65535, 1048577, 4294967296,bigand256keach warn on line 3 with the advice. 881/0.test_sockopt/_corkt_rcvbuf_window: 76/0.config_routesandconfig_runtimepass.HOST_TESTS_EXPECTEDis 474 (+predict_window).Size (default, pinned /opt/amiga)
RAM: +4 B per
AmiSana2If(attached interface) and +4 B perAmiIfConfig.check-stack-frames.sh: clean, 48 roots.Predicted window
build/host/tests/netstack/predict_window [pool= payload= consumers= bps= rtt= growrtt= hw= mss= windowmax= peerscale=]uses the same arithmetic and prints key=value. With no arguments it prints the #89 A1200 arms: 4096-packet pool, 3 live sockets, 2 ms handshake.No hardware or emulator run. Draft: do not merge.
🤖 Generated with Claude Code